[J-core] What's involved with putting serial ports on GPIO pins?

Geoff Salmon gsalmon at se-instruments.com
Fri Apr 29 12:37:39 EDT 2016

On 16-04-29 12:56 AM, Rob Landley wrote:
> Going through fun config tweaks you could do, I remember that when we
> couldn't figure out why Numato's USB serial implementation didn't work
> on mac (answer, because they hadn't wired up the rts/cts lines between
> their serial chip and the USB chip, so the resulting USB packets said
> that nothing was ever ready to send or clear to send so MacOS X blocked
> endlessly and their stty doesn't have a "disable rts/cts" option)...
> Anyway, Jeff worked around this for the liuxcon japan talk by sticking a
> serial port on two gpio pins. and soldering two wires and maybe a couple
> resistors to a conventional serial port connector.
> Could we document how to do that? It seems like a fun "get your hands
> dirty" electrical experiment that's less likely to fry the board than
> some. :)

I can describe how to connect the uartlite to the gpio pins within the 
bitstream. I don't know what needs to be physically connected to the 
gpio pins. Something like this 
http://store.digilentinc.com/pmodusbuart-usb-to-uart-interface/ might work.

The soc_gen tool controls which signals connect to which pins, and the 
soc_top/targets/boards/mimas_v2/design.edn file describes the vhdl 
soc_gen will generate for the mimas_v2 board. The syntax of the .edn 
format is described here https://github.com/edn-format/edn. It consists 
mostly of the data literals of a language called clojure. edn is to 
clojure what json is to javascript.

If you search the web for "edn" in the context of FPGAs or VHDL, you 
might find EDIF https://en.wikipedia.org/wiki/EDIF which can be stored 
in an .edn file. This has nothing to do with the edn files that soc_gen 
reads in.

We don't yet have documentation for what soc_gen expects to see in the 
edn file, but I'll describe enough below to change where the uartlite's 
pins connect.

Instantiating the uartlite

Near the bottom of the mimas_v2/design.edn is

   {:class "uartlite"
    :name "uart0"
    :base-addr 0xabcd0100
    :irq 1
    :generics {"intcfg" 1
               "bps" 19.2e3}
    :dt-props {:current-speed (19200)
               :port-number (0)}
    :dt-stdout true}

This map is inside the :devices [...] vector, which lists the devices to 
connect to the soc. This particular map instantiates a uartlite named 
"uart0". soc_gen uses a global namespace of signals and will create 
signals by concatenating the device name, an underscore, and the 
device's port names. The uartlite entity has ports "tx" and "rx", so 
there will be signals "uart0_tx" and "uart0_rx" for this uartlite which 
should be connected to something in the design. (the uartlite entity 
also has other ports like rst, clk, irq, and a data bus, but soc_gen 
handles those ports differently and won't create signals like uart0_rst 
or uart0_clk).

Look in the generated soc_top/targets/boards/mimas_v2/devices.vhd. At 
the bottom is

     uart0 : entity work.uartlitedb(arch)
         generic map (
             bps => 19200.0,
             fclk => CFG_CLK_CPU_FREQ_HZ,
             intcfg => 1
         port map (
             clk => clk_sys,
             db_i => devs_bus_o(DEV_UART0),
             db_o => devs_bus_i(DEV_UART0),
             int => irqs0(1),
             rst => reset,
             rx => uart0_rx,
             tx => uart0_tx

This is the vhdl that instantiates the uartlite entity. Notice the rx 
port connects to uart0_rx and the tx port connects to uart0_tx.

Now we have signals "uart0_tx" and "uart0_rx". One end of each connects 
to the uartlite and we want to connect the other end to something else. 
In this case they are connected directly to fpga pins. The design.edn 
describes how, but first a detour about FPGA pins...


To the xilinx tools, each pin is identified by its location which is a 
letter and number like A8 or F4. The ucf file provided by Numato 
gives each pin a logical name depending on what it connects to on the 
Mimas v2 board. For example the line

     #NET "UART_TX"                    LOC = A8      |  IOSTANDARD = 

tells you that a net on the board named "UART_TX" connects to pin A8. A8 
is the value of the location constraint.

It's much easier to refer to pins by these logical name instead of the 
location. soc_gen maps names to pin locations in a .pins file. I hand 
made soc_top/targets/boards/pins/mimas_v2.pins looking at the 
mimasv2_user_constraints_file.ucf. Similar files can be exported from 
board layout tools. Like the ucf file, the mimas_v2.pins file contains 
lines like


which assign names to the pin locations.

Pin Rules in design.edn

Now back to the mimas_v2/design.edn. At the top there is a map

:pins {:file "../pins/mimas_v2.pins"
        :type :pin-names
        :rules [ ... ]}

The :file path points to the pins file (confusingly, the path is 
relative to soc_top/targets/soc_gen instead of the location of the 
design.edn). Next the :rules vector contains a list of maps. Each map is 
a "rule" that is applied, one at a time in order, to the pins. Each rule 
has a :match value which determines which pin the rules applies to. It 
can be the name of a single pin, a regular expression to match multiple 
pins, or other things. The two rules we are interested in are

{:match "uart_tx" :signal "uart0_rx"}
{:match "uart_rx" :signal "uart0_tx" :attrs {:drive 8 :slew "fast"}}

The first connects the pin uart_tx to the uart0_rx signal within the 
soc. The is the input path to the uartlite.

The second connects the pin uart_rx to the uart0_tx signal within the 
soc. This is the output path.

The rx/tx swapping here is confusing but is something that frequently 
happens with the names tx and rx. Here the names of the nets in Numato's 
ucf file are probably taken from the names of the pins on the UART-USB 
chip on the Mimas v2 board, which are reversed relative to the soc 

Changing Pins Rules and running soc_gen

As an experiment try deleting one of these rules and running "make 
soc_gen" in the soc_top directory (the "soc_gen" section of the 
soc_top/README for what is required to run soc_gen).

If you delete this rule

   {:match "uart_tx" :signal "uart0_rx"}

soc_gen will fail with the error

   ERR: Nothing drives signal uart0_rx used by device.uart0[rx]
   1 builds failed:

After removing the connection between the signal uart0_rx and the pin 
uart_tx, nothing is left to drive the signal. The uart0 expects 
something to be input to its rx port, and soc_gen will fail if nothing 
is connected.

Restore the deleted rule and instead delete the second rule

   {:match "uart_rx" :signal "uart0_tx" :attrs {:drive 8 :slew "fast"}}

and run "make soc_gen" again. This time it should succeed. Try diff-ing 
the soc_top/target/boards/mimas_v2 directory before and after this 
change. You should see the uartlite's tx port is now connected to 'open' 
in devices.vhd

-            tx => uart0_tx
+            tx => open

which in vhdl means it's not connected to anything. In general it's ok 
to ignore an output port of an entity, so soc_gen doesn't print an 
error. There'll also be more changes to the generated pad_ring.vhd and 
soc.vhd: the uart0_tx signal no longer to a port of the pad_ring.vhd. 
pad_ring.vhd is the top-level entity in the design, so its ports are the 
connected pins of the fpga.

Connecting uart to gpio pins

Finally, to connect the uartlite's tx/rx signals to pins of one of the 
gpio headers on the Mimas v2, look for the pin name in the mimas_v2.pins 
file. I'll randomly choose to use these pins:

   IO_P7_6 V8
   IO_P7_5 R8

then modify mimas_v2/design.edn:


   {:match "uart_tx" :signal "uart0_rx"}
   {:match "uart_rx" :signal "uart0_tx" :attrs {:drive 8 :slew "fast"}}

and change the end of the :rules vector from

          {:match ["io_p" n "_" m] :signal ["io_p" n "(" m ")"]
           :attrs {:drive 8 :slew "fast"}}]}


          {:match ["io_p" n "_" m] :signal ["io_p" n "(" m ")"]
           :attrs {:drive 8 :slew "fast"}}

          {:match "io_p7_6" :signal "uart0_rx"}
          {:match "io_p7_5" :signal "uart0_tx" :attrs {:drive 8 :slew 

and rerun "make soc_gen". I've attached a diff of the results.

It was necessary to move the uart0_rx/tx rules after the existing

   {:match ["io_p" n "_" m] :signal ["io_p" n "(" m ")"]
    :attrs {:drive 8 :slew "fast"}}

rule because that rule assigns different signal names to all the io_pN_M 
pins. Rules are applied in order, so by putting the uart0_tx/rx rules 
after this one we override the signal name for the two pins we care about.

Hope that helps. It boils down to changing a few lines in the design.edn 
file and running soc_gen. Let us know if you're able to run soc_gen or 
not. Any feedback will be useful for improving and documenting soc_gen.

- Geoff

-------------- next part --------------
A non-text attachment was scrubbed...
Name: move_uart_pins.diff
Type: text/x-patch
Size: 3994 bytes
Desc: not available
URL: <http://lists.j-core.org/pipermail/j-core/attachments/20160429/67feff35/attachment.bin>

More information about the J-core mailing list