AddingPeripherals.mdwn
[pinmux.git] / docs / AddingPeripherals.mdwn
index 38d8c4edb3cdce4f317382c573af585ed4feacab..f2a405c0a58eabd694d323d9159349d289874b41 100644 (file)
@@ -752,3 +752,147 @@ This generates *one* mkConnection for each multi-entry pintype, and here we
 match up with the "InterfaceMultiBus" class from the specification side,
 where pin entries with numerically matching names were "grouped" into single
 multi-bit declarations.
+
+## Adjusting the BSV Interface to a get/put style
+
+For various reasons, related to BSV not permitting wires to be connected
+back-to-back inside the pinmux code, a get/put style of interface had to
+be done.  This requirement has a knock-on effect up the chain into the
+actual peripheral code.  So now the actual interface (Ifc\_sdram\_out)
+has to be converted.  All straight methods (outputs) are converted to Get,
+and Action methods (inputs) converted to Put.  Also, it is just plain
+sensible not to use Bool but to use Bit#, and for the pack / unpack to
+be carried out in the interface.  After conversion, the code looks like this:
+
+    interface Ifc_sdram_out;
+        (*always_enabled, always_ready*)
+        interface Put#(Bit#(64)) ipad_sdr_din;
+        interface Get#(Bit#(64)) osdr_dout;
+        interface Get#(Bit#(64)) osdr_den_n;
+        interface Get#(Bit#(1))  osdr_cke;
+        interface Get#(Bit#(1))  osdr_cs_n;
+        interface Get#(Bit#(1))  osdr_ras_n;
+        interface Get#(Bit#(1))  osdr_cas_n;
+        interface Get#(Bit#(1))  osdr_we_n;
+        interface Get#(Bit#(8))  osdr_dqm;
+        interface Get#(Bit#(2))  osdr_ba;
+        interface Get#(Bit#(13)) osdr_addr;
+
+        method Bit#(9) sdram_sdio_ctrl;
+        interface Clock sdram_clk;
+    endinterface
+
+Note that osdr\_den\_n is now **64** bit **not** 8, as discussed above.
+After conversion, the code looks like this:
+
+    interface Ifc_sdram_out ifc_sdram_out;
+
+        interface ipad_sdr_din = interface Put
+            method Action put(Bit#(64) in)
+                sdr_cntrl.ipad_sdr_din <= in;
+            endmethod
+        endinterface;
+
+        interface osdr_dout = interface Get
+          method ActionValue#(Bit#(64)) get;
+            return sdr_cntrl.osdr_dout();
+          endmethod
+        endinterface;
+
+        interface osdr_den_n = interface Get
+            method ActionValue#(Bit#(64)) get;
+                Bit#(64) temp;
+                for (int i=0; i<8; i=i+1) begin
+                  temp[i*8] = sdr_cntrl.osdr_den_n[i];
+                end
+                return temp;
+            endmethod
+        endinterface;
+
+        interface osdr_cke = interface Get
+          method ActionValue#(Bit#(1)) get;
+            return pack(sdr_cntrl.osdr_cke());
+          endmethod
+        endinterface;
+
+        ...
+        ...
+
+    endinterface;
+
+Note that the data input is quite straightforward, as is data out,
+and cke: whether 8-bit, 13-bit or 64-bit, the conversion process is
+mundane, with only Bool having to be converted to Bit#(1) with a call
+to pack.  The data-enable however is a massive hack: whilst 64 enable
+lines come in, only every 8th bit is actually utilised and passed
+through.  Whether this should be changed is a matter for debate that
+is outside of the scope of this document.
+
+Lastly for this phase we have two anomalous interfaces, exposing
+the control registers and the clock, that have been moved out of
+Ifc\_sdram\_out, to the level above (Ifc\_sdr\_slave) so that the sole
+set of interfaces exposed for inter-connection by the auto-generator is
+related exclusively to the actual pins.  Resolution of these two issues
+is currently outside of the scope of this document.
+
+## Clock synchronisation
+
+Astute readers, if their heads have not exploded by this point, will
+have noticed earlier that the SDRAM instance was declared with an entirely
+different clock domain from slow peripherals.  Whilst the pinmux is
+completely combinatorial logic that is entirely unclocked, BSV has no
+means of informing a *module* of this fact, and consequently a weird
+null-clock splicing trick is required.
+
+This code is again auto-generated.  However, it is slightly tricky to
+describe and there are several edge-cases, including ones where the
+peripheral is a slow peripheral (UART is an example) that is driven
+from a UART clock but it is connected up inside the slow peripherals
+code; FlexBus is a Fast Bus peripheral that needs syncing up; RGB/TTL
+likewise, but JTAG is specifically declared inside the SoC and passed
+through, but its instantiation requires a separate incoming clock.
+
+Examining the similarity between the creation of an SDRAM instance
+and the JTAG instance, the jtag code therefore looks like it is the
+best candidate fit.  However, it passes through a reset signal as well.
+Instead, we modify this to create clk0 but use the slow\_reset
+signal:
+
+    class sdram(PBase):
+
+        def get_clk_spc(self, typ):
+            return "tck, slow_reset"
+
+        def get_clock_reset(self, name, count):
+            return "slow_clock,  slow_reset"
+
+The resultant synchronisation code, that accepts pairs of clock/reset
+tuples in order to take care of the prerequisite null reset and clock
+"synchronisation", looks like this:
+
+        Ifc_sync#(Bit#(1)) sdr0_sdrcke_sync <-mksyncconnection(
+                    clk0, slow_reset, slow_clock, slow_reset);
+        Ifc_sync#(Bit#(1)) sdr0_sdrrasn_sync <-mksyncconnection(
+                    clk0, slow_reset, slow_clock, slow_reset);
+        ...
+        ...
+        Ifc_sync#(Bit#(64)) sdr0_d_in_sync <-mksyncconnection(
+            slow_clock, slow_reset, clk0, slow_reset);
+
+Note that inputs are in reverse order from outputs.  With the inclusion of
+clock synchronisation, automatic chains of mkConnections are set up between
+the actual peripheral and the peripheral side of the pinmux:
+
+    mkConnection(slow_peripherals.sdr0.sdrcke,
+                sdr0_sdrcke_sync.get);
+    mkConnection(sdr0_sdrcke_sync.put,
+                sdr0.ifc_sdram_out.osdr_cke);
+
+Interestingly, if *actual* clock synchronisation were ever to be needed,
+it could easily be taken care of with relatively little extra work.  However,
+it is worth emphasising that the pinmux is *entirely* unclocked zero-reset
+combinatorial logic.
+
+# Conclusion
+
+This is not a small project, by any means.