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 # Adding the peripheral to a chip's pinmux specification
113
114 Next, we add the peripheral to an actual chip's specification. In this
115 case it is to be added to i\_class, so we open src/spec/i\_class.py. The
116 first thing to do is to add a single-mux (dedicated) bank of 92 pins (!)
117 which covers all of the 64-bit Data lines, 13 addresses and supporting
118 bank-selects and control lines. It is added as Bank "D", the next
119 contiguous bank:
120
121 def pinspec():
122 pinbanks = {
123 'A': (28, 4),
124 'B': (18, 4),
125 'C': (24, 1),
126 'D': (92, 1), <---
127 }
128 fixedpins = {
129 'CTRL_SYS': [
130
131 This declares the width of the pinmux to one (a dedicated peripheral
132 bank). Note in passing that A and B are both 4-entry.
133 Next, an SDRAM interface is conveniently added to the chip's pinmux
134 with two simple lines of code:
135
136 ps.gpio("", ('B', 0), 0, 0, 18)
137 ps.flexbus1("", ('B', 0), 1, spec=flexspec)
138
139 ps.flexbus2("", ('C', 0), 0)
140
141 ps.sdram1("", ('D', 0), 0) <--
142 ps.sdram3("", ('D', 35), 0) <--
143
144 Note that the first argument is blank, indicating that this is the only
145 SDRAM interface to be added. If more than one SDRAM interface is desired
146 they would be numbered from 0 and identified by their suffix. The second
147 argument is a tuple of (Bank Name, Bank Row Number), and the third argument
148 is the pinmux column (which in this case must be zero).
149
150 At the top level the following command is then run:
151
152 $ python src/pinmux_generator.py -o i_class -s i_class
153
154 The output may be found in the ./i\_class subdirectory, and it is worth
155 examining the i\_class.mdwn file. A table named "Bank D" will have been
156 created and it is worth just showing the first few entries here:
157
158 | Pin | Mux0 | Mux1 | Mux2 | Mux3 |
159 | --- | ----------- | ----------- | ----------- | ----------- |
160 | 70 | D SDR_SDRDQM0 |
161 | 71 | D SDR_SDRDQM1 |
162 | 72 | D SDR_SDRDQM2 |
163 | 73 | D SDR_SDRDQM3 |
164 | 74 | D SDR_SDRDQM4 |
165 | 75 | D SDR_SDRDQM5 |
166 | 76 | D SDR_SDRDQM6 |
167 | 77 | D SDR_SDRDQM7 |
168 | 78 | D SDR_SDRD0 |
169 | 79 | D SDR_SDRD1 |
170 | 80 | D SDR_SDRD2 |
171 | 81 | D SDR_SDRD3 |
172 | 82 | D SDR_SDRD4 |
173 | 83 | D SDR_SDRD5 |
174 | 84 | D SDR_SDRD6 |
175 | 85 | D SDR_SDRD7 |
176 | 86 | D SDR_SDRAD0 |
177 | 87 | D SDR_SDRAD1 |
178
179 Returning to the definition of sdram1 and sdram3, this table clearly
180 corresponds to the functions in src/spec/pinfunctions.py which is
181 exactly what we want. It is however extremely important to verify.
182
183 This basically concludes the first stage of adding a peripheral to
184 the pinmux / autogenerator tool. It allows peripherals to be assessed
185 for viability prior to actually committing the engineering resources
186 to their deployment.
187
188 # Adding the code auto-generators.
189
190 With the specification now created and well-defined (and now including
191 the SDRAM interface), the next completely separate phase is to auto-generate
192 the code that will drop an SDRAM instance onto the fabric of the SoC.
193
194 This particular peripheral is classified as a "Fast Bus" peripheral.
195 "Slow" peripherals will need to be the specific topic of an alternative
196 document, however the principles are the same.
197
198 The first requirement is that the pins from the peripheral side be connected
199 through to IO cells. This can be verified by running the pinmux code
200 generator (to activate "default" behaviour), just to see what happens:
201
202 $ python src/pinmux_generator.py -o i_class
203
204 Files are auto-generated in ./i\_class/bsv\_src and it is recommended
205 to examine the pinmux.bsv file in an editor, and search for occurrences
206 of the string "sdrd63". It can clearly be seen that an interface
207 named "PeripheralSideSDR" has been auto-generated:
208
209 // interface declaration between SDR and pinmux
210 (*always_ready,always_enabled*)
211 interface PeripheralSideSDR;
212 interface Put#(Bit#(1)) sdrdqm0;
213 interface Put#(Bit#(1)) sdrdqm1;
214 interface Put#(Bit#(1)) sdrdqm2;
215 interface Put#(Bit#(1)) sdrdqm3;
216 interface Put#(Bit#(1)) sdrdqm4;
217 interface Put#(Bit#(1)) sdrdqm5;
218 interface Put#(Bit#(1)) sdrdqm6;
219 interface Put#(Bit#(1)) sdrdqm7;
220 interface Put#(Bit#(1)) sdrd0_out;
221 interface Put#(Bit#(1)) sdrd0_outen;
222 interface Get#(Bit#(1)) sdrd0_in;
223 ....
224 ....
225 endinterface
226
227 Note that for the data lines, that where in the sdram1 specification function
228 the signals were named "SDRDn+, out, out-enable *and* in interfaces/methods
229 have been created, as these will be *directly* connected to the I/O pads.
230
231 Further down the file we see the *actual* connection to the I/O pad (cell).
232 An example:
233
234 // --------------------
235 // ----- cell 161 -----
236
237 // output muxer for cell idx 161
238 cell161_mux_out=
239 wrsdr_sdrd63_out;
240
241 // outen muxer for cell idx 161
242 cell161_mux_outen=
243 wrsdr_sdrd63_outen; // bi-directional
244
245 // priority-in-muxer for cell idx 161
246
247 rule assign_wrsdr_sdrd63_in_on_cell161;
248 wrsdr_sdrd63_in<=cell161_mux_in;
249 endrule
250
251 Here, given that this is a "dedicated" cell (with no muxing), we have
252 *direct* assignment of all three signals (in, out, outen). 2-way, 3-way
253 and 4-way muxing creates the required priority-muxing for inputs and
254 straight-muxing for outputs, however in this instance, a deliberate
255 pragmatic decision is being taken not to put 92 pins of 133mhz+ signalling
256 through muxing.
257
258 In examining the slow\_peripherals.bsv file