X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=docs%2FAddingPeripherals.mdwn;h=f2a405c0a58eabd694d323d9159349d289874b41;hb=5341c63c67a38fdad72c717ac8584ff31f024b16;hp=88d93198e2120699e48ae4a4742cd1f5779e45e9;hpb=92cd44bcd9a90ea2f1eb0f49c9a498fde092f331;p=pinmux.git diff --git a/docs/AddingPeripherals.mdwn b/docs/AddingPeripherals.mdwn index 88d9319..f2a405c 100644 --- a/docs/AddingPeripherals.mdwn +++ b/docs/AddingPeripherals.mdwn @@ -29,6 +29,31 @@ for the SDRAM peripheral, we find its interface is defined as follows: interface Clock sdram_clk; endinterface +Also note further down that the code to map, for example, the 8 actual dqm +pins into a single 8-bit interface has also been auto-generated. Generally +it is a good idea to verify that correspondingly the three data in/out/outen +interfaces have also been correctly generated. + + interface sdr = interface PeripheralSideSDR + + ... + ... + + interface dqm = interface Put#(8) + method Action put(Bit#(8) in); + wrsdr_sdrdqm0 <= in[0]; + wrsdr_sdrdqm1 <= in[1]; + wrsdr_sdrdqm2 <= in[2]; + wrsdr_sdrdqm3 <= in[3]; + wrsdr_sdrdqm4 <= in[4]; + wrsdr_sdrdqm5 <= in[5]; + wrsdr_sdrdqm6 <= in[6]; + wrsdr_sdrdqm7 <= in[7]; + endmethod + endinterface; + + endinterface; + So now we go to src/spec/pinfunctions.py and add a corresponding function that returns a list of all of the required pin signals. However, we note that it is a huge number of pins so a decision is made to split it into @@ -713,12 +738,12 @@ so it is *presently* done by hand: ret = [PBase._mk_pincon(self, name, count, typ)] assert typ == 'fast' # TODO slow? for pname, stype, ptype in [ - ('sdrdqm', 'osdr_dqm', 'out'), - ('sdrba', 'osdr_ba', 'out'), - ('sdrad', 'osdr_addr', 'out'), - ('sdrd_out', 'osdr_dout', 'out'), - ('sdrd_in', 'ipad_sdr_din', 'in'), - ('sdrd_out_en', 'osdr_den_n', 'out'), + ('dqm', 'osdr_dqm', 'out'), + ('ba', 'osdr_ba', 'out'), + ('ad', 'osdr_addr', 'out'), + ('d_out', 'osdr_dout', 'out'), + ('d_in', 'ipad_sdr_din', 'in'), + ('d_out_en', 'osdr_den_n', 'out'), ]: ret.append(self._mk_vpincon(name, count, typ, ptype, pname, "ifc_sdram_out.{0}".format(stype))) @@ -727,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.