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
def sdram3(suffix, bank):
buspins = []
- inout = []
+ inout = []
for i in range(12, 13):
buspins.append("SDRAD%d+" % i)
for i in range(8, 64):
through to IO cells. This can be verified by running the pinmux code
generator (to activate "default" behaviour), just to see what happens:
- $ python src/pinmux_generator.py -o i_class
+ $ python src/pinmux_generator.py -o i_class
Files are auto-generated in ./i\_class/bsv\_src and it is recommended
to examine the pinmux.bsv file in an editor, and search for occurrences
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.
So first, we must identify the nearest similar class. FlexBus looks
like a good candidate, so we take a copy of src/bsv/peripheral\_gen/flexbus.py
called sdram.py. The simplest next step is to global/search/replace
-"flexbus" with "sdram". At this phase, despite knowing that it will
+"flexbus" with "sdram", and for peripheral instance declaration replace
+"fb" with "sdr". At this phase, despite knowing that it will
auto-generate the wrong code, we add it as a "supported" peripheral
at the bottom of src/bsv/peripheral\_gen/base.py, in the "PFactory"
(Peripheral Factory) class:
peripheral may be declared in the "fast" fabric and connected up to the
relevant and required "fast" bus.
+## Connecting in the fabric
+
+Now we can begin the process of systematically inserting the correct
+"voodoo magic" incantations that, as far as this auto-generator tool is
+concerned, are just bits of ASCII text. In this particular instance, an
+SDRAM peripheral happened to already be *in* the SoC's BSV source code,
+such that the process of adding it to the tool is primarily one of
+*conversion*.
+
+**Please note that it is NOT recommended to do two tasks at once.
+It is strongly recommended to add any new peripheral to a pre-existing
+verified project, manually, by hand, and ONLY then to carry out a
+conversion process to have this tool understand how to auto-generate
+the fabric**
+
+So examining the i\_class socgen.bsv file, we also open up
+src/bsv/bsv\_lib/soc\_template.bsv in side-by-side windows of maximum
+80 characters in width each, and *respect the coding convention for
+this exact purpose*, can easily fit two such windows side-by-side
+*as well as* a third containing the source code files that turn that
+same template into its corresponding output.
+
+We can now begin by searching for strings "SDRAM" and "sdr" in both
+the template and the auto-generated socgen.bsv file. The first such
+encounter is the import, in the template:
+
+ `ifdef BOOTROM
+ import BootRom ::*;
+ `endif
+ `ifdef SDRAM <-- xxxx
+ import sdr_top :: *; <-- xxxx
+ `endif <-- xxxx
+ `ifdef BRAM
+
+This we can **remove**, and drop the corresponding code-fragment into
+the sdram slowimport function:
+
+ class sdram(PBase):
+
+ def slowimport(self):
+ return "import sdr_top::*;" <--
+
+ def num_axi_regs32(self):
+
+Now we re-run the auto-generator tool and confirm that, indeed, the
+ifdef'd code is gone and replaced with an unconditional import:
+
+ import mqspi :: *;
+ import sdr_top::*; <--
+ import Uart_bs :: *;
+ import RS232_modified::*;
+ import mspi :: *;
+
+Progress! Next, we examine the instance declaration clause. Remember
+that we cut/paste the flexbus class, so we are expecting to find code
+that declares the sdr0 instance as a FlexBus peripheral. We are
+also looking for the hand-created code that is to be *replaced*. Sure enough:
+
+ AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
+ sdr0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor; <--
+ AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
+ fb0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor;
+ ...
+ ...
+ `ifdef BOOTROM
+ BootRom_IFC bootrom <-mkBootRom;
+ `endif
+ `ifdef SDRAM <--
+ Ifc_sdr_slave sdram<- mksdr_axi4_slave(clk0); <--
+ `endif <--
+
+So, the mksdr\_axi4\_slave call we *remove* from the template and cut/paste
+it into the sdram class's mkfast_peripheral function, making sure to
+substitute the hard-coded instance name "sdram" with a python-formatted
+template that can insert numerical instance identifiers, should it ever
+be desired that there be more than one SDRAM peripheral put into a chip:
+
+class sdram(PBase):
+
+ ...
+ ...
+ def mkfast_peripheral(self):
+ return "Ifc_sdr_slave sdr{0} <- mksdr_axi4_slave(clk0);"
+
+Re-run the tool and check that the correct-looking code has been created:
+
+ Ifc_sdr_slave sdr0 <- mksdr_axi4_slave(clk0); <--
+ AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
+ fb0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor;
+ Ifc_rgbttl_dummy lcd0 <- mkrgbttl_dummy();
+
+The next thing to do: searching for the string "sdram\_out" shows that the
+original hand-generated code contains (contained) a declaration of the
+SDRAM Interface, presumably to which, when compiling to run on an FPGA,
+the SDRAM interface would be connected at the top level. Through this
+interface, connections would be done *by hand* to the IO pads, whereas
+now they are to be connected *automatically* (on the peripheral side)
+to the IO pads in the pinmux. However, at the time of writing this is
+not fully understood by the author, so the fastifdecl and extfastifinstance
+functions are modified to generate the correct output but the code is
+*commented out*
+
+ def extfastifinstance(self, name, count):
+ return "// TODO" + self._extifinstance(name, count, "_out", "", True,
+ ".if_sdram_out")
+
+ def fastifdecl(self, name, count):
+ return "// (*always_ready*) interface " + \
+ "Ifc_sdram_out sdr{0}_out;".format(count)
+
+Also the corresponding (old) manual declarations of sdram\_out
+removed from the template:
+
+ `ifdef SDRAM <-- xxxx
+ (*always_ready*) interface Ifc_sdram_out sdram_out; <-- xxxx
+ `endif <-- xxxx
+ ...
+ ...
+ `ifdef SDRAM <--- xxxx
+ interface sdram_out=sdram.ifc_sdram_out; <--- xxxx
+ `endif <--- xxxx
+
+Next, again searching for signs of the "hand-written" code, we encounter
+the fabric connectivity, which wires the SDRAM to the AXI4. We note however
+that there is not just one AXI slave device but *two*: one for the SDRAM
+itself and one for *configuring* the SDRAM. We therefore need to be
+quite careful about assigning these, as will be subsequently explained.
+First however, the two AXI4 slave interfaces of this peripheral are
+declared:
+
+ class sdram(PBase):
+
+ ...
+ ...
+ def _mk_connection(self, name=None, count=0):
+ return ["sdr{0}.axi4_slave_sdram",
+ "sdr{0}.axi4_slave_cntrl_reg"]
+
+Note that, again, in case multiple instances are ever to be added, the
+python "format" string "{0}" is inserted so that it can be substituted
+with the numerical identifier suffix. Also note that the order
+of declaration of these two AXI4 slave is **important**.
+
+Re-running the auto-generator tool, we note the following output has
+been created, and match it against the corresponding hand-generated (old)
+code:
+
+ `ifdef SDRAM
+ mkConnection (fabric.v_to_slaves
+ [fromInteger(valueOf(Sdram_slave_num))],
+ sdram.axi4_slave_sdram); //
+ mkConnection (fabric.v_to_slaves
+ [fromInteger(valueOf(Sdram_cfg_slave_num))],
+ sdram.axi4_slave_cntrl_reg); //
+ `endif
+
+ // fabric connections
+ mkConnection (fabric.v_to_slaves
+ [fromInteger(valueOf(SDR0_fastslave_num))],
+ sdr0.axi4_slave_sdram);
+ mkConnection (fabric.v_to_slaves
+ [fromInteger(valueOf(SDR0_fastslave_num))],
+ sdr0.axi4_slave_cntrl_reg);
+
+Immediately we can spot an issue: whilst the correctly-named slave(s) have
+been added, they have been added with the *same* fabric slave index. This
+is unsatisfactory and needs resolving.
+
+Here we need to explain a bit more about what is going on. The fabric
+on an AXI4 Bus is allocated numerical slave numbers, and each slave is
+also allocated a memory-mapped region that must be resolved in a bi-directional
+fashion. i.e whenever a particular memory region is accessed, the AXI
+slave peripheral responsible for dealing with it **must** be correctly
+identified. So this requires some further crucial information, which is
+the size of the region that is to be allocated to each slave device. Later
+this will be extended to being part of the specification, but for now
+it is auto-allocated based on the size. As a huge hack, it is allocated
+in 32-bit chunks, as follows:
+
+class sdram(PBase):
+
+ def num_axi_regs32(self):
+ return [0x400000, # defines an entire memory range (hack...)
+ 12] # defines the number of configuration regs
+
+So after running the autogenerator again, to confirm that this has
+generated the correct code, we examine several files, starting with
+fast\+memory\_map.bsv:
+
+ /*====== Fast peripherals Memory Map ======= */
+ `define SDR0_0_Base 'h50000000
+ `define SDR0_0_End 'h5FFFFFFF // 4194304 32-bit regs
+ `define SDR0_1_Base 'h60000000
+ `define SDR0_1_End 'h600002FF // 12 32-bit regs
+
+This looks slightly awkward (and in need of an external specification
+section for addresses) but is fine: the range is 1GB for the main
+map and covers 12 32-bit registers for the SDR Config map.
+Next we note the slave numbering:
+
+ typedef 0 SDR0_0__fastslave_num;
+ typedef 1 SDR0_1__fastslave_num;
+ typedef 2 FB0_fastslave_num;
+ typedef 3 LCD0_fastslave_num;
+ typedef 3 LastGen_fastslave_num;
+ typedef TAdd#(LastGen_fastslave_num,1) Sdram_slave_num;
+ typedef TAdd#(Sdram_slave_num ,`ifdef SDRAM 1 `else 0 `endif )
+ Sdram_cfg_slave_num;
+
+Again this looks reasonable and we may subsequently (carefully! noting
+the use of the TAdd# chain!) remove the #define for Sdram\_cfg\_slave\_num.
+The next phase is to examine the fn\_addr\_to\_fastslave\_num function,
+where we note that there were *two* hand-created sections previously,
+now joined by two *auto-generated* sections:
+
+ function Tuple2 #(Bool, Bit#(TLog#(Num_Fast_Slaves)))
+ fn_addr_to_fastslave_num (Bit#(`PADDR) addr);
+
+ if(addr>=`SDRAMMemBase && addr<=`SDRAMMemEnd)
+ return tuple2(True,fromInteger(valueOf(Sdram_slave_num))); <--
+ else if(addr>=`DebugBase && addr<=`DebugEnd)
+ return tuple2(True,fromInteger(valueOf(Debug_slave_num))); <--
+ `ifdef SDRAM
+ else if(addr>=`SDRAMCfgBase && addr<=`SDRAMCfgEnd )
+ return tuple2(True,fromInteger(valueOf(Sdram_cfg_slave_num)));
+ `endif
+
+ ...
+ ...
+ if(addr>=`SDR0_0_Base && addr<=`SDR0_0_End) <--
+ return tuple2(True,fromInteger(valueOf(SDR0_0__fastslave_num)));
+ else
+ if(addr>=`SDR0_1_Base && addr<=`SDR0_1_End) <--
+ return tuple2(True,fromInteger(valueOf(SDR0_1__fastslave_num)));
+ else
+ if(addr>=`FB0Base && addr<=`FB0End)
+
+Now, here is where, in a slightly unusual unique set of circumstances, we
+cannot just remove all instances of this address / typedef from the template
+code. Looking in the shakti-core repository's src/lib/MemoryMap.bsv file,
+the SDRAMMemBase macro is utilise in the is\_IO\_Addr function. So as a
+really bad hack, which will need to be properly resolved, whilst the
+hand-generated sections from fast\_tuple2\_template.bsv are removed,
+and the corresponding (now redundant) defines in src/core/core\_parameters.bsv
+are commented out, some temporary typedefs to deal with the name change are
+also added:
+
+ `define SDRAMMemBase SDR0_0_Base
+ `define SDRAMMemEnd SDR0_0_End
+
+This needs to be addressed (pun intended) including being able to specify
+the name(s) of the configuration parameters, as well as specifying which
+memory map range they must be added to.
+
+Now however finally, after carefully comparing the hard-coded fabric
+connections to what was formerly named sdram, we may remove the mkConnections
+that drop sdram.axi4\_slave\_sdram and its associated cntrl reg from
+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.