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
pragmatic decision is being taken not to put 92 pins of 133mhz+ signalling
through muxing.
+## Making the peripheral a "MultiBus" peripheral
+
+The sheer number of signals coming out of PeripheralSideSDR is so unwieldy
+that something has to be done. We therefore create a "MultiBus" interface
+such that the pinmux knows which pins are grouped together by name.
+This is done in src/bsv/interface\_decl.py.
+
+The MultiBus code is quite sophisticated, in that buses can be identified
+by pattern, and removed one by one. The *remaining* pins are left behind
+as individual single-bit pins. Starting from a copy of InterfaceFlexBus
+as the most similar code, a cut/paste copy is taken and the new class
+InterfaceSDRAM created:
+
+ class InterfaceSDRAM(InterfaceMultiBus, Interface):
+
+ def __init__(self, ifacename, pinspecs, ganged=None, single=False):
+ Interface.__init__(self, ifacename, pinspecs, ganged, single)
+ InterfaceMultiBus.__init__(self, self.pins)
+ self.add_bus(False, ['dqm', None, None],
+ "Bit#({0})", "sdrdqm")
+ self.add_bus(True, ['d_out', 'd_out_en', 'd_in'],
+ "Bit#({0})", "sdrd")
+ self.add_bus(False, ['ad', None, None],
+ "Bit#({0})", "sdrad")
+ self.add_bus(False, ['ba', None, None],
+ "Bit#({0})", "sdrba")
+
+ def ifacedef2(self, *args):
+ return InterfaceMultiBus.ifacedef2(self, *args)
+
+Here, annoyingly, the data bus is a mess, requiring identification of
+the three separate names for in, out and outen. The prefix "sdrd" is however
+unique and obvious in its purpose: anything beginning with "sdrd" is
+treated as a multi-bit bus, and a template for declaring a BSV type is
+given that is automatically passed the numerical quantity of pins detected
+that start with the word "sdrd".
+
+Note that it is critical to lexicographically identify pins correctly,
+so sdrdqm is done **before** sdrd.
+
+Once the buses have been identified the peripheral can be added into
+class Interfaces:
+
+ class Interfaces(InterfacesBase, PeripheralInterfaces):
+ """ contains a list of interface definitions
+ """
+
+ def __init__(self, pth=None):
+ InterfacesBase.__init__(self, Interface, pth,
+ {'gpio': InterfaceGPIO,
+ 'fb': InterfaceFlexBus,
+ 'sdr': InterfaceSDRAM, <--
+
+Running the tool again results in a much smaller, tidier output
+that will be a lot less work, later. Note the automatic inclusion of
+the correct length multi-bit interfaces. d-out/in/out-en is identified
+as 64-bit, ad is identified as 13-bit, ba as 2 and dqm as 8.
+
+ // interface declaration between SDR and pinmux
+ (*always_ready,always_enabled*)
+ interface PeripheralSideSDR;
+ interface Put#(Bit#(1)) sdrcke;
+ interface Put#(Bit#(1)) sdrrasn;
+ interface Put#(Bit#(1)) sdrcasn;
+ interface Put#(Bit#(1)) sdrwen;
+ interface Put#(Bit#(1)) sdrcsn0;
+
+ interface Put#(Bit#(8)) dqm;
+ interface Put#(Bit#(64)) d_out;
+ interface Put#(Bit#(64)) d_out_en;
+ interface Get#(Bit#(64)) d_in;
+ interface Put#(Bit#(13)) ad;
+ interface Put#(Bit#(2)) ba;
+
+ endinterface
+
+## Adding the peripheral
+
In examining the slow\_peripherals.bsv file, there should at this stage
be no sign of an SDRAM peripheral having been added, at all. This is
because it is missing from the peripheral\_gen side of the tool.
the soc\_template.bsv file.
## Connecting up the pins
+
+We are still not done! It is however worth pointing out that if this peripheral
+were not wired into the pinmux, we would in fact be finished. However there
+is a task that (previously having been left to outside tools) now needs to
+be specified, which is to connect the sdram's pins, declared in this
+instance in Ifc\_sdram\_out, and the PeripheralSideSDR instance that
+was kindly / strategically / thoughtfully / absolutely-necessarily exported
+from slow\_peripherals for exactly this purpose.
+
+Recall earlier that we took a cut/paste copy of the flexbus.py code. If
+we now examine socgen.bsv we find that it contains connections to pins
+that match the FlexBus specification, not SDRAM. So, returning to the
+declaration of the Ifc\_sdram\_out interface, we first identify the
+single-bit output-only pins, and add a mapping table between them:
+
+ class sdram(PBase):
+
+ def pinname_out(self, pname):
+ return {'sdrwen': 'ifc_sdram_out.osdr_we_n',
+ 'sdrcsn0': 'ifc_sdram_out.osdr_cs_n',
+ 'sdrcke': 'ifc_sdram_out.osdr_cke',
+ 'sdrrasn': 'ifc_sdram_out.osdr_ras_n',
+ 'sdrcasn': 'ifc_sdram_out.osdr_cas_n',
+ }.get(pname, '')
+
+Re-running the tool confirms that the relevant mkConnections are generated:
+
+ //sdr {'action': True, 'type': 'out', 'name': 'sdrcke'}
+ mkConnection(slow_peripherals.sdr0.sdrcke,
+ sdr0_sdrcke_sync.get);
+ mkConnection(sdr0_sdrcke_sync.put,
+ sdr0.ifc_sdram_out.osdr_cke);
+ //sdr {'action': True, 'type': 'out', 'name': 'sdrrasn'}
+ mkConnection(slow_peripherals.sdr0.sdrrasn,
+ sdr0_sdrrasn_sync.get);
+ mkConnection(sdr0_sdrrasn_sync.put,
+ sdr0.ifc_sdram_out.osdr_ras_n);
+
+Next, the multi-value entries are tackled (both in and out). At present
+the code is messy, as it does not automatically detect the multiple numerical
+declarations, nor that the entries are sometimes inout (in, out, outen),
+so it is *presently* done by hand:
+
+ class sdram(PBase):
+
+ def _mk_pincon(self, name, count, typ):
+ ret = [PBase._mk_pincon(self, name, count, typ)]
+ assert typ == 'fast' # TODO slow?
+ for pname, stype, ptype in [
+ ('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)))
+
+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.
+