add sdram dual axi4 configs
[pinmux.git] / docs / AddingPeripherals.mdwn
index b381cde6f046beb1726e44cf02ec3160ca958ed0..e1b470d2fef99d9d9778a51e16aef3402365935b 100644 (file)
@@ -75,7 +75,7 @@ the address lines to 13-bit wide:
 
     def sdram3(suffix, bank):
         buspins = []
-        inout = [] 
+        inout = []
         for i in range(12, 13):
             buspins.append("SDRAD%d+" % i)
         for i in range(8, 64):
@@ -215,7 +215,7 @@ The first requirement is that the pins from the peripheral side be connected
 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
@@ -285,4 +285,228 @@ is added.
 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".  
+"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:
+
+    from gpio import gpio
+    from rgbttl import rgbttl
+    from flexbus import flexbus
+    from sdram import sdram       <--
+
+    for k, v in {'uart': uart,
+                 'rs232': rs232,
+                 'sdr': sdram,
+                 'twi': twi,
+                 'quart': quart,
+
+Note that the name "SDR" matches with the prefix used in the pinspec
+declaration, back in src/spec/pinfunctions.py, except lower-cased.  Once this
+is done, and the auto-generation tool re-run, examining the
+slow\_peripherals.bsv file again shows the following (correct) and only
+the following (correct) additions:
+
+    method Bit#(1) quart0_intr;
+    method Bit#(1) quart1_intr;
+    interface GPIO_config#(28) pad_configa;
+    interface PeripheralSideSDR sdr0;        <--
+    interface PeripheralSideFB fb0;
+
+    ....
+    ....
+    interface iocell_side=pinmux.iocell_side;
+    interface sdr0 = pinmux.peripheral_side.sdr;   <--
+    interface fb0 = pinmux.peripheral_side.fb;
+
+These automatically-generated declarations are sufficient to "pass through"
+the SDRAM "Peripheral Side", which as we know from examination of the code
+is directly connected to the relevant IO pad cells, so that the *actual*
+peripheral may be declared in the "fast" fabric and connected up to the
+relevant and required "fast" bus.
+
+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
+
+