AddingPeripherals.mdwn
[pinmux.git] / docs / AddingPeripherals.mdwn
1 # How to add a new peripheral
2
3 This document describes the process of adding a new peripheral to
4 the pinmux and auto-generator, through a worked example, adding
5 SDRAM.
6
7 # Creating the specifications
8
9 The tool is split into two halves that are separated by tab-separated
10 files. The first step is therefore to add a function that defines
11 the peripheral as a python function. That implies in turn that the
12 pinouts of the peripheral must be known. Looking at the BSV code
13 for the SDRAM peripheral, we find its interface is defined as follows:
14
15 interface Ifc_sdram_out;
16 (*always_enabled,always_ready*)
17 method Action ipad_sdr_din(Bit#(64) pad_sdr_din);
18 method Bit#(9) sdram_sdio_ctrl();
19 method Bit#(64) osdr_dout();
20 method Bit#(8) osdr_den_n();
21 method Bool osdr_cke();
22 method Bool osdr_cs_n();
23 method Bool osdr_ras_n ();
24 method Bool osdr_cas_n ();
25 method Bool osdr_we_n ();
26 method Bit#(8) osdr_dqm ();
27 method Bit#(2) osdr_ba ();
28 method Bit#(13) osdr_addr ();
29 interface Clock sdram_clk;
30 endinterface
31
32 So now we go to src/spec/pinfunctions.py and add a corresponding function
33 that returns a list of all of the required pin signals. However, we note
34 that it is a huge number of pins so a decision is made to split it into
35 groups: sdram1, sdram2 and sdram3. Firstly, sdram1, covering the base
36 functionality:
37
38 def sdram1(suffix, bank):
39 buspins = []
40 inout = []
41 for i in range(8):
42 pname = "SDRDQM%d*" % i
43 buspins.append(pname)
44 for i in range(8):
45 pname = "SDRD%d*" % i
46 buspins.append(pname)
47 inout.append(pname)
48 for i in range(12):
49 buspins.append("SDRAD%d+" % i)
50 for i in range(2):
51 buspins.append("SDRBA%d+" % i)
52 buspins += ['SDRCKE+', 'SDRRASn+', 'SDRCASn+', 'SDRWEn+',
53 'SDRCSn0++']
54 return (buspins, inout)
55
56 This function, if used on its own, would define an 8-bit SDRAM bus with
57 12-bit addressing. Checking off the names against the corresponding BSV
58 definition we find that most of them are straightforward. Outputs
59 must have a "+" after the name (in the python representation), inputs
60 must have a "-".
61
62 However we run smack into an interesting brick-wall with the in/out pins.
63 In/out pins which are routed through the same IO pad need a *triplet* of
64 signals: one input wire, one output wire and *one direction control wire*.
65 Here however we find that the SDRAM controller, which is a wrapper around
66 the opencores SDRAM controller, has a *banked* approach to direction-control
67 that will need to be dealt with, later. So we do *not* make the mistake
68 of adding 8 SDRDENx pins: the BSV code will need to be modified to
69 add 64 one-for-one enabling pins. We do not also make the mistake of
70 adding separate unidirectional "in" and separate unidirectional "out" signals
71 under different names, as the pinmux code is a *PAD* centric tool.
72
73 The second function extends the 8-bit data bus to 64-bits, and extends
74 the address lines to 13-bit wide:
75
76 def sdram3(suffix, bank):
77 buspins = []
78 inout = []
79 for i in range(12, 13):
80 buspins.append("SDRAD%d+" % i)
81 for i in range(8, 64):
82 pname = "SDRD%d*" % i
83 buspins.append(pname)
84 inout.append(pname)
85 return (buspins, inout)
86
87 In this way, alternative SDRAM controller implementations can use sdram1
88 on its own; implementors may add "extenders" (named sdram2, sdram4) that
89 cover extra functionality, and, interestingly, in a pinbank scenario,
90 the number of pins on any given GPIO bank may be kept to a sane level.
91
92 The next phase is to add the (now supported) peripheral to the list
93 of pinspecs at the bottom of the file, so that it can actually be used:
94
95 pinspec = (('IIS', i2s),
96 ('MMC', emmc),
97 ('FB', flexbus1),
98 ('FB', flexbus2),
99 ('SDR', sdram1),
100 ('SDR', sdram2),
101 ('SDR', sdram3), <---
102 ('EINT', eint),
103 ('PWM', pwm),
104 ('GPIO', gpio),
105 )
106
107 This gives a declaration that any time the function(s) starting with
108 "sdram" are used to add pins to a pinmux, it will be part of the
109 "SDR" peripheral. Note that flexbus is similarly subdivided into
110 two groups.
111
112 Note however that due to a naming convention issue, interfaces must
113 be declared with names that are lexicographically unique even in
114 subsets of their names. i.e two interfaces, one named "SD" which is
115 shorthand for SDMMC and another named "SDRAM" may *not* be added:
116 the first has to be the full "SDMMC" or renamed to "MMC".
117
118 # Adding the peripheral to a chip's pinmux specification
119
120 Next, we add the peripheral to an actual chip's specification. In this
121 case it is to be added to i\_class, so we open src/spec/i\_class.py. The
122 first thing to do is to add a single-mux (dedicated) bank of 92 pins (!)
123 which covers all of the 64-bit Data lines, 13 addresses and supporting
124 bank-selects and control lines. It is added as Bank "D", the next
125 contiguous bank:
126
127 def pinspec():
128 pinbanks = {
129 'A': (28, 4),
130 'B': (18, 4),
131 'C': (24, 1),
132 'D': (92, 1), <---
133 }
134 fixedpins = {
135 'CTRL_SYS': [
136
137 This declares the width of the pinmux to one (a dedicated peripheral
138 bank). Note in passing that A and B are both 4-entry.
139 Next, an SDRAM interface is conveniently added to the chip's pinmux
140 with two simple lines of code:
141
142 ps.gpio("", ('B', 0), 0, 0, 18)
143 ps.flexbus1("", ('B', 0), 1, spec=flexspec)
144
145 ps.flexbus2("", ('C', 0), 0)
146
147 ps.sdram1("", ('D', 0), 0) <--
148 ps.sdram3("", ('D', 35), 0) <--
149
150 Note that the first argument is blank, indicating that this is the only
151 SDRAM interface to be added. If more than one SDRAM interface is desired
152 they would be numbered from 0 and identified by their suffix. The second
153 argument is a tuple of (Bank Name, Bank Row Number), and the third argument
154 is the pinmux column (which in this case must be zero).
155
156 At the top level the following command is then run:
157
158 $ python src/pinmux_generator.py -o i_class -s i_class
159
160 The output may be found in the ./i\_class subdirectory, and it is worth
161 examining the i\_class.mdwn file. A table named "Bank D" will have been
162 created and it is worth just showing the first few entries here:
163
164 | Pin | Mux0 | Mux1 | Mux2 | Mux3 |
165 | --- | ----------- | ----------- | ----------- | ----------- |
166 | 70 | D SDR_SDRDQM0 |
167 | 71 | D SDR_SDRDQM1 |
168 | 72 | D SDR_SDRDQM2 |
169 | 73 | D SDR_SDRDQM3 |
170 | 74 | D SDR_SDRDQM4 |
171 | 75 | D SDR_SDRDQM5 |
172 | 76 | D SDR_SDRDQM6 |
173 | 77 | D SDR_SDRDQM7 |
174 | 78 | D SDR_SDRD0 |
175 | 79 | D SDR_SDRD1 |
176 | 80 | D SDR_SDRD2 |
177 | 81 | D SDR_SDRD3 |
178 | 82 | D SDR_SDRD4 |
179 | 83 | D SDR_SDRD5 |
180 | 84 | D SDR_SDRD6 |
181 | 85 | D SDR_SDRD7 |
182 | 86 | D SDR_SDRAD0 |
183 | 87 | D SDR_SDRAD1 |
184
185 Returning to the definition of sdram1 and sdram3, this table clearly
186 corresponds to the functions in src/spec/pinfunctions.py which is
187 exactly what we want. It is however extremely important to verify.
188
189 Lastly, the peripheral is a "fast" peripheral, i.e. it must not
190 be added to the "slow" peripherals AXI4-Lite Bus, so must be added
191 to the list of "fast" peripherals, here:
192
193 ps = PinSpec(pinbanks, fixedpins, function_names,
194 ['lcd', 'jtag', 'fb', 'sdr']) <--
195
196 # Bank A, 0-27
197 ps.gpio("", ('A', 0), 0, 0, 28)
198
199 This basically concludes the first stage of adding a peripheral to
200 the pinmux / autogenerator tool. It allows peripherals to be assessed
201 for viability prior to actually committing the engineering resources
202 to their deployment.
203
204 # Adding the code auto-generators.
205
206 With the specification now created and well-defined (and now including
207 the SDRAM interface), the next completely separate phase is to auto-generate
208 the code that will drop an SDRAM instance onto the fabric of the SoC.
209
210 This particular peripheral is classified as a "Fast Bus" peripheral.
211 "Slow" peripherals will need to be the specific topic of an alternative
212 document, however the principles are the same.
213
214 The first requirement is that the pins from the peripheral side be connected
215 through to IO cells. This can be verified by running the pinmux code
216 generator (to activate "default" behaviour), just to see what happens:
217
218 $ python src/pinmux_generator.py -o i_class
219
220 Files are auto-generated in ./i\_class/bsv\_src and it is recommended
221 to examine the pinmux.bsv file in an editor, and search for occurrences
222 of the string "sdrd63". It can clearly be seen that an interface
223 named "PeripheralSideSDR" has been auto-generated:
224
225 // interface declaration between SDR and pinmux
226 (*always_ready,always_enabled*)
227 interface PeripheralSideSDR;
228 interface Put#(Bit#(1)) sdrdqm0;
229 interface Put#(Bit#(1)) sdrdqm1;
230 interface Put#(Bit#(1)) sdrdqm2;
231 interface Put#(Bit#(1)) sdrdqm3;
232 interface Put#(Bit#(1)) sdrdqm4;
233 interface Put#(Bit#(1)) sdrdqm5;
234 interface Put#(Bit#(1)) sdrdqm6;
235 interface Put#(Bit#(1)) sdrdqm7;
236 interface Put#(Bit#(1)) sdrd0_out;
237 interface Put#(Bit#(1)) sdrd0_outen;
238 interface Get#(Bit#(1)) sdrd0_in;
239 ....
240 ....
241 endinterface
242
243 Note that for the data lines, that where in the sdram1 specification function
244 the signals were named "SDRDn+, out, out-enable *and* in interfaces/methods
245 have been created, as these will be *directly* connected to the I/O pads.
246
247 Further down the file we see the *actual* connection to the I/O pad (cell).
248 An example:
249
250 // --------------------
251 // ----- cell 161 -----
252
253 // output muxer for cell idx 161
254 cell161_mux_out=
255 wrsdr_sdrd63_out;
256
257 // outen muxer for cell idx 161
258 cell161_mux_outen=
259 wrsdr_sdrd63_outen; // bi-directional
260
261 // priority-in-muxer for cell idx 161
262
263 rule assign_wrsdr_sdrd63_in_on_cell161;
264 wrsdr_sdrd63_in<=cell161_mux_in;
265 endrule
266
267 Here, given that this is a "dedicated" cell (with no muxing), we have
268 *direct* assignment of all three signals (in, out, outen). 2-way, 3-way
269 and 4-way muxing creates the required priority-muxing for inputs and
270 straight-muxing for outputs, however in this instance, a deliberate
271 pragmatic decision is being taken not to put 92 pins of 133mhz+ signalling
272 through muxing.
273
274 ## Making the peripheral a "MultiBus" peripheral
275
276 The sheer number of signals coming out of PeripheralSideSDR is so unwieldy
277 that something has to be done. We therefore create a "MultiBus" interface
278 such that the pinmux knows which pins are grouped together by name.
279 This is done in src/bsv/interface\_decl.py.
280
281 The MultiBus code is quite sophisticated, in that buses can be identified
282 by pattern, and removed one by one. The *remaining* pins are left behind
283 as individual single-bit pins. Starting from a copy of InterfaceFlexBus
284 as the most similar code, a cut/paste copy is taken and the new class
285 InterfaceSDRAM created:
286
287 class InterfaceSDRAM(InterfaceMultiBus, Interface):
288
289 def __init__(self, ifacename, pinspecs, ganged=None, single=False):
290 Interface.__init__(self, ifacename, pinspecs, ganged, single)
291 InterfaceMultiBus.__init__(self, self.pins)
292 self.add_bus(False, ['dqm', None, None],
293 "Bit#({0})", "sdrdqm")
294 self.add_bus(True, ['d_out', 'd_out_en', 'd_in'],
295 "Bit#({0})", "sdrd")
296 self.add_bus(False, ['ad', None, None],
297 "Bit#({0})", "sdrad")
298 self.add_bus(False, ['ba', None, None],
299 "Bit#({0})", "sdrba")
300
301 def ifacedef2(self, *args):
302 return InterfaceMultiBus.ifacedef2(self, *args)
303
304 Here, annoyingly, the data bus is a mess, requiring identification of
305 the three separate names for in, out and outen. The prefix "sdrd" is however
306 unique and obvious in its purpose: anything beginning with "sdrd" is
307 treated as a multi-bit bus, and a template for declaring a BSV type is
308 given that is automatically passed the numerical quantity of pins detected
309 that start with the word "sdrd".
310
311 Note that it is critical to lexicographically identify pins correctly,
312 so sdrdqm is done **before** sdrd.
313
314 Once the buses have been identified the peripheral can be added into
315 class Interfaces:
316
317 class Interfaces(InterfacesBase, PeripheralInterfaces):
318 """ contains a list of interface definitions
319 """
320
321 def __init__(self, pth=None):
322 InterfacesBase.__init__(self, Interface, pth,
323 {'gpio': InterfaceGPIO,
324 'fb': InterfaceFlexBus,
325 'sdr': InterfaceSDRAM, <--
326
327 Running the tool again results in a much smaller, tidier output
328 that will be a lot less work, later. Note the automatic inclusion of
329 the correct length multi-bit interfaces. d-out/in/out-en is identified
330 as 64-bit, ad is identified as 13-bit, ba as 2 and dqm as 8.
331
332 // interface declaration between SDR and pinmux
333 (*always_ready,always_enabled*)
334 interface PeripheralSideSDR;
335 interface Put#(Bit#(1)) sdrcke;
336 interface Put#(Bit#(1)) sdrrasn;
337 interface Put#(Bit#(1)) sdrcasn;
338 interface Put#(Bit#(1)) sdrwen;
339 interface Put#(Bit#(1)) sdrcsn0;
340
341 interface Put#(Bit#(8)) dqm;
342 interface Put#(Bit#(64)) d_out;
343 interface Put#(Bit#(64)) d_out_en;
344 interface Get#(Bit#(64)) d_in;
345 interface Put#(Bit#(13)) ad;
346 interface Put#(Bit#(2)) ba;
347
348 endinterface
349
350 ## Adding the peripheral
351
352 In examining the slow\_peripherals.bsv file, there should at this stage
353 be no sign of an SDRAM peripheral having been added, at all. This is
354 because it is missing from the peripheral\_gen side of the tool.
355
356 However, as the slow\_peripherals module takes care of the IO cells
357 (because it contains a declared and configured instance of the pinmux
358 package), signals from the pinmux PeripheralSideSDR instance need
359 to be passed *through* the slow peripherals module as an external
360 interface. This will happen automatically once a code-generator class
361 is added.
362
363 So first, we must identify the nearest similar class. FlexBus looks
364 like a good candidate, so we take a copy of src/bsv/peripheral\_gen/flexbus.py
365 called sdram.py. The simplest next step is to global/search/replace
366 "flexbus" with "sdram", and for peripheral instance declaration replace
367 "fb" with "sdr". At this phase, despite knowing that it will
368 auto-generate the wrong code, we add it as a "supported" peripheral
369 at the bottom of src/bsv/peripheral\_gen/base.py, in the "PFactory"
370 (Peripheral Factory) class:
371
372 from gpio import gpio
373 from rgbttl import rgbttl
374 from flexbus import flexbus
375 from sdram import sdram <--
376
377 for k, v in {'uart': uart,
378 'rs232': rs232,
379 'sdr': sdram,
380 'twi': twi,
381 'quart': quart,
382
383 Note that the name "SDR" matches with the prefix used in the pinspec
384 declaration, back in src/spec/pinfunctions.py, except lower-cased. Once this
385 is done, and the auto-generation tool re-run, examining the
386 slow\_peripherals.bsv file again shows the following (correct) and only
387 the following (correct) additions:
388
389 method Bit#(1) quart0_intr;
390 method Bit#(1) quart1_intr;
391 interface GPIO_config#(28) pad_configa;
392 interface PeripheralSideSDR sdr0; <--
393 interface PeripheralSideFB fb0;
394
395 ....
396 ....
397 interface iocell_side=pinmux.iocell_side;
398 interface sdr0 = pinmux.peripheral_side.sdr; <--
399 interface fb0 = pinmux.peripheral_side.fb;
400
401 These automatically-generated declarations are sufficient to "pass through"
402 the SDRAM "Peripheral Side", which as we know from examination of the code
403 is directly connected to the relevant IO pad cells, so that the *actual*
404 peripheral may be declared in the "fast" fabric and connected up to the
405 relevant and required "fast" bus.
406
407 ## Connecting in the fabric
408
409 Now we can begin the process of systematically inserting the correct
410 "voodoo magic" incantations that, as far as this auto-generator tool is
411 concerned, are just bits of ASCII text. In this particular instance, an
412 SDRAM peripheral happened to already be *in* the SoC's BSV source code,
413 such that the process of adding it to the tool is primarily one of
414 *conversion*.
415
416 **Please note that it is NOT recommended to do two tasks at once.
417 It is strongly recommended to add any new peripheral to a pre-existing
418 verified project, manually, by hand, and ONLY then to carry out a
419 conversion process to have this tool understand how to auto-generate
420 the fabric**
421
422 So examining the i\_class socgen.bsv file, we also open up
423 src/bsv/bsv\_lib/soc\_template.bsv in side-by-side windows of maximum
424 80 characters in width each, and *respect the coding convention for
425 this exact purpose*, can easily fit two such windows side-by-side
426 *as well as* a third containing the source code files that turn that
427 same template into its corresponding output.
428
429 We can now begin by searching for strings "SDRAM" and "sdr" in both
430 the template and the auto-generated socgen.bsv file. The first such
431 encounter is the import, in the template:
432
433 `ifdef BOOTROM
434 import BootRom ::*;
435 `endif
436 `ifdef SDRAM <-- xxxx
437 import sdr_top :: *; <-- xxxx
438 `endif <-- xxxx
439 `ifdef BRAM
440
441 This we can **remove**, and drop the corresponding code-fragment into
442 the sdram slowimport function:
443
444 class sdram(PBase):
445
446 def slowimport(self):
447 return "import sdr_top::*;" <--
448
449 def num_axi_regs32(self):
450
451 Now we re-run the auto-generator tool and confirm that, indeed, the
452 ifdef'd code is gone and replaced with an unconditional import:
453
454 import mqspi :: *;
455 import sdr_top::*; <--
456 import Uart_bs :: *;
457 import RS232_modified::*;
458 import mspi :: *;
459
460 Progress! Next, we examine the instance declaration clause. Remember
461 that we cut/paste the flexbus class, so we are expecting to find code
462 that declares the sdr0 instance as a FlexBus peripheral. We are
463 also looking for the hand-created code that is to be *replaced*. Sure enough:
464
465 AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
466 sdr0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor; <--
467 AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
468 fb0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor;
469 ...
470 ...
471 `ifdef BOOTROM
472 BootRom_IFC bootrom <-mkBootRom;
473 `endif
474 `ifdef SDRAM <--
475 Ifc_sdr_slave sdram<- mksdr_axi4_slave(clk0); <--
476 `endif <--
477
478 So, the mksdr\_axi4\_slave call we *remove* from the template and cut/paste
479 it into the sdram class's mkfast_peripheral function, making sure to
480 substitute the hard-coded instance name "sdram" with a python-formatted
481 template that can insert numerical instance identifiers, should it ever
482 be desired that there be more than one SDRAM peripheral put into a chip:
483
484 class sdram(PBase):
485
486 ...
487 ...
488 def mkfast_peripheral(self):
489 return "Ifc_sdr_slave sdr{0} <- mksdr_axi4_slave(clk0);"
490
491 Re-run the tool and check that the correct-looking code has been created:
492
493 Ifc_sdr_slave sdr0 <- mksdr_axi4_slave(clk0); <--
494 AXI4_Slave_to_FlexBus_Master_Xactor_IFC #(`PADDR, `DATA, `USERSPACE)
495 fb0 <- mkAXI4_Slave_to_FlexBus_Master_Xactor;
496 Ifc_rgbttl_dummy lcd0 <- mkrgbttl_dummy();
497
498 The next thing to do: searching for the string "sdram\_out" shows that the
499 original hand-generated code contains (contained) a declaration of the
500 SDRAM Interface, presumably to which, when compiling to run on an FPGA,
501 the SDRAM interface would be connected at the top level. Through this
502 interface, connections would be done *by hand* to the IO pads, whereas
503 now they are to be connected *automatically* (on the peripheral side)
504 to the IO pads in the pinmux. However, at the time of writing this is
505 not fully understood by the author, so the fastifdecl and extfastifinstance
506 functions are modified to generate the correct output but the code is
507 *commented out*
508
509 def extfastifinstance(self, name, count):
510 return "// TODO" + self._extifinstance(name, count, "_out", "", True,
511 ".if_sdram_out")
512
513 def fastifdecl(self, name, count):
514 return "// (*always_ready*) interface " + \
515 "Ifc_sdram_out sdr{0}_out;".format(count)
516
517 Also the corresponding (old) manual declarations of sdram\_out
518 removed from the template:
519
520 `ifdef SDRAM <-- xxxx
521 (*always_ready*) interface Ifc_sdram_out sdram_out; <-- xxxx
522 `endif <-- xxxx
523 ...
524 ...
525 `ifdef SDRAM <--- xxxx
526 interface sdram_out=sdram.ifc_sdram_out; <--- xxxx
527 `endif <--- xxxx
528
529 Next, again searching for signs of the "hand-written" code, we encounter
530 the fabric connectivity, which wires the SDRAM to the AXI4. We note however
531 that there is not just one AXI slave device but *two*: one for the SDRAM
532 itself and one for *configuring* the SDRAM. We therefore need to be
533 quite careful about assigning these, as will be subsequently explained.
534 First however, the two AXI4 slave interfaces of this peripheral are
535 declared:
536
537 class sdram(PBase):
538
539 ...
540 ...
541 def _mk_connection(self, name=None, count=0):
542 return ["sdr{0}.axi4_slave_sdram",
543 "sdr{0}.axi4_slave_cntrl_reg"]
544
545 Note that, again, in case multiple instances are ever to be added, the
546 python "format" string "{0}" is inserted so that it can be substituted
547 with the numerical identifier suffix. Also note that the order
548 of declaration of these two AXI4 slave is **important**.
549
550 Re-running the auto-generator tool, we note the following output has
551 been created, and match it against the corresponding hand-generated (old)
552 code:
553
554 `ifdef SDRAM
555 mkConnection (fabric.v_to_slaves
556 [fromInteger(valueOf(Sdram_slave_num))],
557 sdram.axi4_slave_sdram); //
558 mkConnection (fabric.v_to_slaves
559 [fromInteger(valueOf(Sdram_cfg_slave_num))],
560 sdram.axi4_slave_cntrl_reg); //
561 `endif
562
563 // fabric connections
564 mkConnection (fabric.v_to_slaves
565 [fromInteger(valueOf(SDR0_fastslave_num))],
566 sdr0.axi4_slave_sdram);
567 mkConnection (fabric.v_to_slaves
568 [fromInteger(valueOf(SDR0_fastslave_num))],
569 sdr0.axi4_slave_cntrl_reg);
570
571 Immediately we can spot an issue: whilst the correctly-named slave(s) have
572 been added, they have been added with the *same* fabric slave index. This
573 is unsatisfactory and needs resolving.
574
575 Here we need to explain a bit more about what is going on. The fabric
576 on an AXI4 Bus is allocated numerical slave numbers, and each slave is
577 also allocated a memory-mapped region that must be resolved in a bi-directional
578 fashion. i.e whenever a particular memory region is accessed, the AXI
579 slave peripheral responsible for dealing with it **must** be correctly
580 identified. So this requires some further crucial information, which is
581 the size of the region that is to be allocated to each slave device. Later
582 this will be extended to being part of the specification, but for now
583 it is auto-allocated based on the size. As a huge hack, it is allocated
584 in 32-bit chunks, as follows:
585
586 class sdram(PBase):
587
588 def num_axi_regs32(self):
589 return [0x400000, # defines an entire memory range (hack...)
590 12] # defines the number of configuration regs
591
592 So after running the autogenerator again, to confirm that this has
593 generated the correct code, we examine several files, starting with
594 fast\+memory\_map.bsv:
595
596 /*====== Fast peripherals Memory Map ======= */
597 `define SDR0_0_Base 'h50000000
598 `define SDR0_0_End 'h5FFFFFFF // 4194304 32-bit regs
599 `define SDR0_1_Base 'h60000000
600 `define SDR0_1_End 'h600002FF // 12 32-bit regs
601
602 This looks slightly awkward (and in need of an external specification
603 section for addresses) but is fine: the range is 1GB for the main
604 map and covers 12 32-bit registers for the SDR Config map.
605 Next we note the slave numbering:
606
607 typedef 0 SDR0_0__fastslave_num;
608 typedef 1 SDR0_1__fastslave_num;
609 typedef 2 FB0_fastslave_num;
610 typedef 3 LCD0_fastslave_num;
611 typedef 3 LastGen_fastslave_num;
612 typedef TAdd#(LastGen_fastslave_num,1) Sdram_slave_num;
613 typedef TAdd#(Sdram_slave_num ,`ifdef SDRAM 1 `else 0 `endif )
614 Sdram_cfg_slave_num;
615
616 Again this looks reasonable and we may subsequently (carefully! noting
617 the use of the TAdd# chain!) remove the #define for Sdram\_cfg\_slave\_num.
618 The next phase is to examine the fn\_addr\_to\_fastslave\_num function,
619 where we note that there were *two* hand-created sections previously,
620 now joined by two *auto-generated* sections:
621
622 function Tuple2 #(Bool, Bit#(TLog#(Num_Fast_Slaves)))
623 fn_addr_to_fastslave_num (Bit#(`PADDR) addr);
624
625 if(addr>=`SDRAMMemBase && addr<=`SDRAMMemEnd)
626 return tuple2(True,fromInteger(valueOf(Sdram_slave_num))); <--
627 else if(addr>=`DebugBase && addr<=`DebugEnd)
628 return tuple2(True,fromInteger(valueOf(Debug_slave_num))); <--
629 `ifdef SDRAM
630 else if(addr>=`SDRAMCfgBase && addr<=`SDRAMCfgEnd )
631 return tuple2(True,fromInteger(valueOf(Sdram_cfg_slave_num)));
632 `endif
633
634 ...
635 ...
636 if(addr>=`SDR0_0_Base && addr<=`SDR0_0_End) <--
637 return tuple2(True,fromInteger(valueOf(SDR0_0__fastslave_num)));
638 else
639 if(addr>=`SDR0_1_Base && addr<=`SDR0_1_End) <--
640 return tuple2(True,fromInteger(valueOf(SDR0_1__fastslave_num)));
641 else
642 if(addr>=`FB0Base && addr<=`FB0End)
643
644 Now, here is where, in a slightly unusual unique set of circumstances, we
645 cannot just remove all instances of this address / typedef from the template
646 code. Looking in the shakti-core repository's src/lib/MemoryMap.bsv file,
647 the SDRAMMemBase macro is utilise in the is\_IO\_Addr function. So as a
648 really bad hack, which will need to be properly resolved, whilst the
649 hand-generated sections from fast\_tuple2\_template.bsv are removed,
650 and the corresponding (now redundant) defines in src/core/core\_parameters.bsv
651 are commented out, some temporary typedefs to deal with the name change are
652 also added:
653
654 `define SDRAMMemBase SDR0_0_Base
655 `define SDRAMMemEnd SDR0_0_End
656
657 This needs to be addressed (pun intended) including being able to specify
658 the name(s) of the configuration parameters, as well as specifying which
659 memory map range they must be added to.
660
661 Now however finally, after carefully comparing the hard-coded fabric
662 connections to what was formerly named sdram, we may remove the mkConnections
663 that drop sdram.axi4\_slave\_sdram and its associated cntrl reg from
664 the soc\_template.bsv file.
665
666 ## Connecting up the pins
667
668 We are still not done! It is however worth pointing out that if this peripheral
669 were not wired into the pinmux, we would in fact be finished. However there
670 is a task that (previously having been left to outside tools) now needs to
671 be specified, which is to connect the sdram's pins, declared in this
672 instance in Ifc\_sdram\_out, and the PeripheralSideSDR instance that
673 was kindly / strategically / thoughtfully / absolutely-necessarily exported
674 from slow\_peripherals for exactly this purpose.
675
676 Recall earlier that we took a cut/paste copy of the flexbus.py code. If
677 we now examine socgen.bsv we find that it contains connections to pins
678 that match the FlexBus specification, not SDRAM. So, returning to the
679 declaration of the Ifc\_sdram\_out interface, we first identify the
680 single-bit output-only pins, and add a mapping table between them:
681
682 class sdram(PBase):
683
684 def pinname_out(self, pname):
685 return {'sdrwen': 'ifc_sdram_out.osdr_we_n',
686 'sdrcsn0': 'ifc_sdram_out.osdr_cs_n',
687 'sdrcke': 'ifc_sdram_out.osdr_cke',
688 'sdrrasn': 'ifc_sdram_out.osdr_ras_n',
689 'sdrcasn': 'ifc_sdram_out.osdr_cas_n',
690 }.get(pname, '')
691
692 Re-running the tool confirms that the relevant mkConnections are generated:
693
694 //sdr {'action': True, 'type': 'out', 'name': 'sdrcke'}
695 mkConnection(slow_peripherals.sdr0.sdrcke,
696 sdr0_sdrcke_sync.get);
697 mkConnection(sdr0_sdrcke_sync.put,
698 sdr0.ifc_sdram_out.osdr_cke);
699 //sdr {'action': True, 'type': 'out', 'name': 'sdrrasn'}
700 mkConnection(slow_peripherals.sdr0.sdrrasn,
701 sdr0_sdrrasn_sync.get);
702 mkConnection(sdr0_sdrrasn_sync.put,
703 sdr0.ifc_sdram_out.osdr_ras_n);
704
705 Next, the multi-value entries are tackled (both in and out). At present
706 the code is messy, as it does not automatically detect the multiple numerical
707 declarations, nor that the entries are sometimes inout (in, out, outen),
708 so it is *presently* done by hand:
709
710 class sdram(PBase):
711
712 def _mk_pincon(self, name, count, typ):
713 ret = [PBase._mk_pincon(self, name, count, typ)]
714 assert typ == 'fast' # TODO slow?
715 for pname, stype, ptype in [
716 ('sdrdqm', 'osdr_dqm', 'out'),
717 ('sdrba', 'osdr_ba', 'out'),
718 ('sdrad', 'osdr_addr', 'out'),
719 ('sdrd_out', 'osdr_dout', 'out'),
720 ('sdrd_in', 'ipad_sdr_din', 'in'),
721 ('sdrd_out_en', 'osdr_den_n', 'out'),
722 ]:
723 ret.append(self._mk_vpincon(name, count, typ, ptype, pname,
724 "ifc_sdram_out.{0}".format(stype)))
725
726 This generates *one* mkConnection for each multi-entry pintype, and here we
727 match up with the "InterfaceMultiBus" class from the specification side,
728 where pin entries with numerically matching names were "grouped" into single
729 multi-bit declarations.