mention page number of vgbbd
[libreriscv.git] / docs / pinmux.mdwn
index fa770aa8599d021aa94d5397c348d5025d9f96b9..40f4e9097795801901fde45b11d0a5a4218845cb 100644 (file)
@@ -6,7 +6,12 @@ Links:
 * <https://www10.edacafe.com/book/ASIC/CH02/CH02.7.php>
 * <https://ftp.libre-soc.org/Pin_Control_Subsystem_Overview.pdf>
 * <https://bugs.libre-soc.org/show_bug.cgi?id=50>
+* <https://bugs.libre-soc.org/show_bug.cgi?id=750>
+* <https://bugs.libre-soc.org/show_bug.cgi?id=762>
 * <https://git.libre-soc.org/?p=c4m-jtag.git;a=tree;hb=HEAD>
+* Extra info: [[/docs/pinmux/temp_pinmux_info]]
+* <https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/stage2.py> - Latest
+manual demo of pinmux generation
 
 Managing IO on an ASIC is nowhere near as simple as on an FPGA.
 An FPGA has built-in IO Pads, the wires terminate inside an
@@ -15,9 +20,14 @@ In an ASIC, you are going to have to do everything yourself.
 In an ASIC, a bi-directional IO Pad requires three wires (in, out,
 out-enable) to be routed right the way from the ASIC, all
 the way to the IO PAD, where only then does a wire bond connect
-it to a single pin.
+it to a single external pin.
 
-[[!img CH02-44.gif]]
+Below, therefore is a (simplified) diagram of what is
+usually contained in an FPGA's bi-directional IO Pad,
+and consequently this is what you must also provide, and explicitly
+wire up in your ASIC's HDL.
+
+[[!img asic_iopad_gen.svg]]
 
 Designing an ASIC, there is no guarantee that the IO pad is
 working when manufactured. Worse, the peripheral could be
@@ -133,7 +143,7 @@ a UART (caveat below):
 The caveat here is that the Resources of the platform actually
 have to have a UART in order for it to be requestable! Thus:
 
-    resources = create_resources()
+    resources = create_resources() # contains resource named "uart"
     asic = ASICPlatform(resources)
     hdl = Blinker()
     asic.build(hdl)
@@ -215,13 +225,14 @@ and triaging of faults.
   pad is working.  If the UART Rx peripheral was faulty
   this would not be possible.
 
-<img src="https://libre-soc.org/shakti/m_class/JTAG/jtag-block.jpg"
-  width=500 />
+[[!img jtag-block.svg ]]
 
 ## C4M JTAG TAP
 
 Staf Verhaegen's Chips4Makers JTAG TAP module includes everything
-needed to create JTAG Boundary Scan Shift Registers.  However,
+needed to create JTAG Boundary Scan Shift Registers,
+as well as the IEEE 1149.1 Finite State Machine to access
+them through TMS, TDO, TDI and TCK Signalling.  However,
 connecting up cores (a hardware term: the equivalent software
 term is "peripherals") on one side and the pads on the other is
 especially confusing, but deceptively simple.  The actual addition
@@ -229,11 +240,11 @@ to the Scan Shift Register is this straightforward:
 
     from c4m.nmigen.jtag.tap import IOType, TAP
 
-    class JTAG(DMITAP, Pins):
-       def __init__(self, pinset, domain, wb_data_wid=32):
+    class JTAG(TAP):
+       def __init__(self):
            TAP.__init__(self, ir_width=4)
-           tx = self.add_io(iotype=IOType.Out, name="uart_tx")
-           rx = self.add_io(iotype=IOType.In, name="uart_rx")
+           self.u_tx = self.add_io(iotype=IOType.Out, name="tx")
+           self.u_rx = self.add_io(iotype=IOType.In, name="rx")
 
 This results in the creation of:
 
@@ -248,17 +259,117 @@ This results in the creation of:
   allow either direct connection between pad and core
   (bypassing JTAG) or interception
 
+During Interception Mode (Scanning) pad and core are connected
+to the Shift Register.  During "Production" Mode, pad and
+core are wired directly to each other (on a per-pin basis,
+for every pin. Clearly this is a lot of work).
+
 It is then your responsibility to:
 
 * connect up each and every peripheral input and output
   to the right IO Core Record in your HDL
 * connect up each and every IO Pad input and output
-  to the right IO Pad in the Platform
+  to the right IO Pad in the Platform.
+* **This does not happen automatically and is not the
+  responsibility of the TAP Interface, it is yours**
+
+The TAP interface connects the **other** side of the pads
+and cores Records: **to the Muxes**.  You **have** to
+connect **your** side of both core and pads Records in
+order for the Scan to be fully functional.
 
 Both of these tasks are painstaking and tedious in the
 extreme if done manually, and prone to either sheer boredom,
 transliteration errors, dyslexia triggering or just utter
-confusion.
+confusion.  Despite this, let us proceed, and, augmenting
+the Blinky example, wire up a JTAG instance:
+
+    class Blinker(Elaboratable): 
+      def elaborate(self, platform):
+          m = Module()
+          m.submodules.jtag = jtag = JTAG()
+
+          # get the records from JTAG instance
+          utx, urx = jtag.u_tx, jtag.u_rx
+          # get the UART resource, mess with the output tx
+          p_uart = platform.request('uart')
+
+          # uart core-side from JTAG
+          intermediary = Signal()
+          m.d.comb += utx.core.o.eq(~intermediary) # invert, for fun
+          m.d.comb += intermediary.eq(urx.core.i) # pass rx to tx
+
+          # wire up the IO Pads (in right direction) to Platform
+          m.d.comb += uart.rx.eq(utx.pad.i) # receive rx from JTAG input pad
+          m.d.comb += utx.pad.o.eq(uart.tx) # transmit tx to JTAG output pad
+          return m
+
+Compared to the non-scan-capable version, which connected UART
+Core Tx and Rx directly to the Platform Resource (and the Platform
+took care of wiring to IO Pads):
+
+* Core HDL is instead wired to the core-side of JTAG Scan
+* JTAG Pad side is instead wired to the Platform
+* (the Platform still takes care of wiring to actual IO Pads)
+
+JTAG TAP capability on UART TX and RX has now been inserted into
+the chain.  Using openocd or other program it is possible to
+send TDI, TMS, TDO and TCK signals according to IEEE 1149.1 in order
+to intercept both the core and IO Pads, both input and output,
+and confirm the correct functionality of one even if the other is
+broken, during ASIC testing.
+
+## Libre-SOC Automatic Boundary Scan
+
+Libre-SOC's JTAG TAP Boundary Scan system is a little more sophisticated:
+it hooks into (replaces) ResourceManager.request(), intercepting the request
+and recording what was requested.  The above manual linkup to JTAG TAP
+is then taken care of **automatically and transparently**, but to
+all intents and purposes looking exactly like a Platform even to
+the extent of taking the exact same list of Resources.
+
+    class Blinker(Elaboratable):
+      def __init__(self, resources):
+          self.jtag = JTAG(resources)
+
+      def elaborate(self, platform):
+          m = Module()
+          m.submodules.jtag = jtag = self.jtag
+
+          # get the UART resource, mess with the output tx
+          uart = jtag.request('uart')
+          intermediary = Signal()
+          m.d.comb += uart.tx.eq(~intermediary) # invert, for fun
+          m.d.comb += intermediary.eq(uart.rx) # pass rx to tx
+
+          return jtag.boundary_elaborate(m, platform)
+
+Connecting up and building the ASIC is as simple as a non-JTAG,
+non-scanning-aware Platform:
+
+    resources = create_resources()
+    asic = ASICPlatform(resources)
+    hdl = Blinker(resources)
+    asic.build(hdl)
+
+The differences:
+
+* The list of resources was also passed to the HDL Module
+  such that JTAG may create a complete identical list
+  of both core and pad matching Pins
+* Resources were requested from the JTAG instance,
+  not the Platform
+* A "magic function" (JTAG.boundary_elaborate) is called
+  which wires up all of the seamlessly intercepted
+  Platform resources to the JTAG core/pads Resources,
+  where the HDL connected to the core side, exactly
+  as if this was a non-JTAG-Scan-aware Platform.
+* ASICPlatform still takes care of connecting to actual
+  IO Pads, except that the Platform.resource requests were
+  triggered "behind the scenes". For that to work it
+  is absolutely essential that the JTAG instance and the
+  ASICPlatform be given the exact same list of Resources.
+
 
 ## Clock synchronisation
 
@@ -274,7 +385,7 @@ Signal, but the external peripheral (known as a PHY in Hardware terminology)
 
 Firstly: note that the Clock will, obviously, also need to be routed
 through JTAG Boundary Scan, because, after all, it is being received
-through just another ordinary IO Pad, after all.  Secondly: note thst
+through just another ordinary IO Pad, after all.  Secondly: note that
 if it didn't, then clock skew would occur for that peripheral because
 although the Data Wires went through JTAG Boundary Scan MUXes, the
 clock did not.  Clearly this would be a problem.
@@ -306,13 +417,269 @@ but to that clock *after going through H Tree Buffers*.  Therefore,
 there will be a lag on the output data compared to the incoming
 (external) clock
 
-# GPIO Muxing
+# Pinmux GPIO Block
+
+The following diagram is an example of a mux'd GPIO block that comes from the
+Ericson presentation on a GPIO architecture.
+
+[[!img gpio-block.svg size="800x"]]
+
+## Our Pinmux Block
+
+The block we are developing is very similar, but is lacking some of
+configuration of the former (due to complexity and time constraints).
+
+The implemented pinmux uses two sub-blocks:
+
+1. A Wishbone controlled N-GPIO block.
+
+1. N-port I/O multiplexer (for current usecase set to 4
+ports).
+
+### Terminology
+
+For clearer explanation, the following definitions will be used in the text.
+As the documentation is actively being written, the experimental code may not
+adhere to these all the time.
+
+* Bank - A group of contiguous pins under a common name.
+* Pin - Bi-directional wire connecting to the chip's pads.
+* Function (pin) - A signal used by the peripheral.
+* Port - Bi-directional signal on the peripheral and pad sides of the
+multiplexer.
+* Muxwidth - Number of input ports to the multiplexers
+* PinMux - Multiplexer for connecting multiple peripheral functions to one IO
+pad.
+
+For example:
+
+A 128-pin chip has 4 banks N/S/E/W corresponding to the 4 sides of the chip.
+Each bank has 32 pins. Each pin can have up to 4 multiplexed functions, thus
+the multiplexer width for each pin is 4.
+
+### PinSpec Class
+
+* <https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/base.py;h=c8fa2b09d650b1b7cfdb499bfe711a3ebaf5848b;hb=HEAD> PinSpec class
+defined here.
+
+PinSpec is a powerful construct designed to hold all the necessary information
+about the chip's banks. This includes:
+
+1. Number of banks
+1. Number of pins per each bank
+1. Peripherals present (UART, GPIO, I2C, etc.)
+1. Mux configuration for every pin (mux0: gpio, mux1: uart tx, etc.)
+1. Peripheral function signal type (out/in/bi)
+
+### Pinouts
+
+* <https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/interfaces.py;h=f5ecf4817ba439b607a1909a4fcb6aa2589e2afd;hb=HEAD> Pinouts class
+defined here.
+
+The Pinspec class inherits from the Pinouts class, which allows to view the
+dictionaries containing bank names and pin information.
+
+* keys() - returns dict_key object of all the pins (summing all the banks) which
+is iterable
+* items() - a dict_key of pins, with each pin's mux information
+
+For example, PinSpec object 'ps' has one bank 'A' with 4 pins
+ps.keys() returns dict_keys([0, 1, 2, 3])
+ps.items() returns
+dict_items([(0, {0: ('GPIOA_A0', 'A'), 1: ('UART0_TX', 'A'),
+                                2: ('TWI0_SDA', 'A')}),
+                   (1, {0: ('GPIOA_A1', 'A'), 1: ('UART0_RX', 'A'),
+                                2: ('TWI0_SCL', 'A')}),
+                       (2, {0: ('GPIOA_A2', 'A')}), (3, {0: ('GPIOA_A3', 'A')})])
+
+### PinGen
+
+pinfunctions.py contains the "pinspec" list containing the Python functions
+which generate the necessary signals for gpio, uart, i2c, etc. (with IOType
+information). PinGen class uses "__call__" and "pinspec" to effectively create a
+Lambda function for generating specified peripheral signals.
+
+## The GPIO block 
+
+*NOTE !* - Need to change 'bank' terminology for the GPIO block in doc and code!
+
+[[!img n-gpio.svg size="600x"]]
+
+The GPIO module is multi-GPIO block integral to the pinmux system.
+To make the block flexible, it has a variable number of of I/Os based on an
+input parameter.
+
+### Configuration Word
+
+After a discussion with Luke on IRC (14th January 2022), new layout of the
+8-bit data word for configuring the GPIO (through WB):
+
+* oe - Output Enable (see the Ericson presentation for the GPIO diagram)
+* ie - Input Enable *(Not used, as IOPad only supports i/o/oe)*
+* puen - Pull-Up resistor enable
+* pden - Pull-Down resistor enable
+* i/o - When configured as output (oe set), this bit sets/clears output. When
+configured as input, shows the current state of input (read-only)
+* bank[2:0] - Bank Select *(only 4 banks used, bank[2] used for JTAG chain)*
+
+### Simultaneous/Packed Configuration
+
+To make the configuration more efficient, multiple GPIOs can be configured with
+one data word. The number of GPIOs in one "row" is dependent on the WB data bus
+*width* and *granuality* (see Wishbone B4 spec, section 3.5 Data Organization
+for more details).
+
+If for example, the data bus is 64-bits wide and granuality is 8, eight GPIO
+configuration bytes - and thus eight GPIOs - can be configured in one go.
+To configure only certain GPIOs, the WB sel signal can be used (see next
+section).
+
+*(NOTE: Currently the code doesn't support granuality higher than 8)*
+
+The diagram below shows the layout of the configuration byte.
+
+[[!img gpio-config-word.jpg size="600x"]]
+
+If the block is created with more GPIOs than can fit in a single data word,
+the next set of GPIOs can be accessed by incrementing the address.
+For example, if 16 GPIOs are instantiated and 64-bit data bus is used, GPIOs
+0-7 are accessed via address 0, whereas GPIOs 8-15 are accessed by address 1.
+
+### Example Memory Map
+
+[[!img gpio-mem-layout.jpg size="600x"]]
+
+The diagrams above show the difference in memory layout between 16-GPIO block
+implemented with 64-bit and 32-bit WB data buses.
+The 64-bit case shows there are two rows with eight GPIOs in each, and it will
+take two writes (assuming simple WB write) to completely configure all 16 GPIOs.
+The 32-bit on the other hand has four address rows, and so will take four write transactions.
+
+64-bit:
+
+* 0x00 - Configure GPIOs  0-7  - requires 8-bit `sel` one bit per GPIO
+* 0x01 - Configure GPIOs  8-15 - requires 8-bit `sel` one bit per GPIO
+
+32-bit:
+
+* 0x00 - Configure GPIOs  0-3 - requires 4-bit `sel` one bit per GPIO
+* 0x01 - Configure GPIOs  4-7 - requires 4-bit `sel` one bit per GPIO
+* 0x02 - Configure GPIOs  8-11 - requires 4-bit `sel` one bit per GPIO
+* 0x03 - Configure GPIOs 12-15 - requires 4-bit `sel` one bit per GPIO
+
+Here is the pseudocode for reading the GPIO
+data structs:
+
+    read_bytes = []
+    for i in range(len(sel)):
+        GPIO_num = adr*len(sel)+i
+        if sel[i]:
+            read_bytes.append(GPIO[GPIO_num])
+        else:
+            read_bytes.append(Const(0, 8))
+    if not wen:
+        dat_r.eq(Cat(read_bytes))
+
+and for writing, slightly different style:
+
+    if wen:
+        write_bytes = []
+        for i in range(len(sel)):
+            GPIO_num = adr*len(sel)+i
+            write_byte = dat_w.bit_select(i*8, 8)
+            if sel[i]:
+                GPIO[GPIO_num].eq(write_byte)
+
+As explained in this video <https://m.youtube.com/watch?v=Pf6gmDQnw_4>
+if each GPIO is mapped to one single byte, and it is assumed that
+the `sel` lines are enough to **always** give byte-level read/write
+then the GPIO number *becomes* the Memory-mapped byte number, hence
+the use of `len(sel)` above.  `len(dat_r)//8` would do as well
+because these should be equal.
+
+
+## The IO Mux block
+
+[[!img iomux-4bank.svg size="600x"]]
+
+This block is an N-to-1 (4-port shown above) mux and it simultaneously connects:
+
+* o/oe signals from one of N peripheral ports, to the pad output port
+
+* i pad port signal to one of N peripheral ports (the rest being set to 0).
+
+The block is then used in a higher-level pinmux block, and instantiated for each
+pin.
+
+## Combined Block
+
+*NOTE !* - Need to change 'bank' terminology for the GPIO block in doc and code!
+
+[[!img pinmux-1pin.svg size="600x"]]
+
+The GPIO and IOMux blocks are combined in a single block called the
+Pinmux block.
+
+By default, bank 0 is hard-wired to the memory-mapped WB bus GPIO. The CPU
+core can just write the configuration word to the GPIO row address. From this
+perspective, it is no different to a conventional GPIO block.
+
+Bank select, allows to switch over the control of the IO pad to
+another peripheral. The peripheral will be given sole connectivity to the
+o/oe/i signals, while additional parameters such as pull up/down will either
+be automatically configured (as the case for I2C), or will be configurable
+via the WB bus. *(This has not been implemented yet, so open to discussion)*
+
+### Bank Select Options
+
+* bank 0 - WB bus has full control (GPIO peripheral)
+* bank 1,2,3 - WB bus only controls puen/pden, periphal gets o/oe/i
+(whether ie should be routed out is not finalised yet)
+
+
+### Adding JTAG BS Chain to the Pinmux block (In Progress)
+
+The JTAG BS chain need to have access to the bank select bits, to allow
+selecting different peripherals during testing. At the same time, JTAG may
+also require access to the WB bus to access GPIO configuration options
+not available to bank 1/2/3 peripherals.
+
+The proposed JTAG BS chain is as follows:
+
+* Connect puen/pden/bank from GPIO block to the IOMux through JTAG BS chain.
+* Connect the i/o/oe pad port from IOMux via JTAG BS chain.
+* (?) Test port for configuring GPIO without WB? - utilising bank bit 2?
+* (?) Way to drive WB via JTAG?
+
+Such a setup would allow the JTAG chain to control the bank select when testing
+connectivity of the peripherals, as well as give full control to the GPIO
+configuration when bank select bit 2 is set.
+
+For the purposes of muxing peripherals, bank select bit 2 is ignored. This
+means that even if JTAG is handed over full control, the peripheral is
+still connected to the GPIO block (via the BS chain).
+
+Signals for various ports:
+
+* WB bus or Periph0: WB data read, data write, address, sel, cyc, stb, ack
+* Periph1/2/3: o,oe,i (puen/pden are only controlled by WB, test port, or
+fixed by functionality; ie not used yet)
+* (?) Test port: bank[2:0], o,oe,i,ie,puen,pden. In addition, internal
+address to access individual GPIOs will be available (this will consist of a
+few bits, as more than 16 GPIOs per block is likely to be to big).
+
+As you can see by the above list, the pinmux block is becoming quite a complex
+beast. If there are suggestions to simplify or reduce some of the signals,
+that will be helpful.
+
+The diagrams above showed 1-bit GPIO connectivity. Below you'll find the
+4-bit case *(NOT IMPLEMENTED YET)*.
 
-[[!img gpio_block.png]]
+[[!img gpio_jtag_4bit.jpg size="600x"]]
 
 # Core/Pad Connection + JTAG Mux
 
 Diagram constructed from the nmigen plat.py file.
 
-[[!img i_o_io_tristate_jtag.JPG]]
+[[!img i_o_io_tristate_jtag.svg ]]