Skip to content

Test SOC Register Map

Jeff Bush edited this page May 7, 2019 · 5 revisions

The Nyuzi core is a component for larger system-on-chip designs and can't do much by itself. To run on FPGA and execute more complex programs, I've created a reference design with some simple peripherals. These are not part of Nyuzi proper.

  • SDRAM controller: Single data rate, 128 MB on the DE2-115 board
  • VGA controller
  • UART
  • PS/2 keyboard controller
  • LED/HEX display
  • SPI/SDMMC controller

There are two buses internally. An AMBA AXI bus connects memory peripherals. The CPU and VGA controller are masters. There are two slaves-- The SDRAM controller and a boot ROM--selected by physical address. A custom I/O bus is used for controlling and configuring peripherals via memory mapped registers. All of these run in a single 50 MHz clock domain.

FPGA Architecture Diagram

The CPU starts execution at the beginning of boot ROM region (source code is in software/bootrom). The boot ROM waits for the host computer to load a program over the serial interface into the beginning of SDRAM, then jumps to address 0. It configures the UART for 921600 baud by default.

The timings for the SDRAM are hardcoded as synthesis parameters, so configuration is not necessary at boot. This project utilizes around 70k logic elements for a single core on an Altera Cyclone IV chip.

Physical Address space:

Range Address
SDRAM (128M) 00000000
07FFFFFF
Boot ROM (8k) FFFEE000
FFFEFFFF
Device Registers FFFF0000
FFFFFFFF

The emulator and verilator environments support many of the peripherals on FPGA. In the device register descriptions below, the 'environment' column indicates which environments support it: F = fpga, E = emulator, V = verilator.

Interrupts

Vector Type Description
0 edge Cosimulation interrupt timer (simulation only)
1 edge Programmable timer
2 level UART RX bytes available
3 level PS/2 controller bytes available
4 edge VGA new frame

LEDs and Misc Devices

Address r/w Environment Description
ffff0000 w F Set value of red LEDs
ffff0004 w F Set value of green LEDs
ffff0008 w F Set value of 7 segment display 0
ffff000c w F Set value of 7 segment display 1
ffff0010 w F Set value of 7 segment display 2
ffff0014 w F Set value of 7 segment display 3
ffff0018 w E Sends interrupt to host via pipe in emulator
ffff001c w V Force loopback UART tx line low 1
  1. When this register is written, the UART loopback TX line will be forced low. This is used by tests to simulate a framing error. This only works in the Verilator environment.
  2. The cosimulation timer only triggers in the Verilator environment. When it triggers, it causes an interrupt on the processor, but also dumps a cosimulation event. The emulator reads this and triggers a synchronized interrupt on its side. This mechanism allows the interrupt to occur on the same instruction boundary in both environments.

Cosimulation Timer

In the verilator environment, the cosimulation timer raises an interrupt and prints a cosimulation event. The emulator will raise the timer when it sees the event. This allows synchronizing the interrupts so they occur at the same instruction boundary in both environments.

Address r/w Environment Description
ffff0020 w V cosimulation interrupt timer interval

UART

This is a bidirectional serial port. It is hardcoded for 8 data bits, one stop bit, and no parity. The clock rate is configured by programming the UART divider register, which configures the number of clocks per bit (it should be set to clocks minus one). The UART runs off the 50 MHz system clock, so the value should be:

divider = (50000000 / baud_rate) - 1
Address r/w Environment Description
ffff0040 r FEV UART status
ffff0044 r F UART read
ffff0048 w FEV UART write
ffff004c w F UART divider

UART status bits:

Bit Meaning
0 Space available in write FIFO
1 Bytes in read FIFO
2 Receive FIFO overrun
3 Receive Framing error

The overrun and framing error bits are cleared when the UART read register is read.

Serial writes (including printfs from software) print to standard out in Verilator and the emulator.

PS/2 Controller

In the Verilator environment, keyboard scancodes are just an incrementing pattern. For the emulator, they are only returned if the framebuffer window is displayed and in focus. For the FPGA, they come from the PS/2 port on the board.

Address r/w Environment Description
ffff0080 r FEV PS/2 Keyboard status. 1 indicates there are scancodes in FIFO.
ffff0084 r FEV PS/2 Keyboard scancode. Remove from FIFO on read.

SD Card

SD GPIO and SD SPI are mutually exclusive. SD GPIO is if BITBANG_SDMMC is set in hardware/fpga/de2_115/de2_115_top.sv, SPI otherwise.

SPI Mode

The SPI controller is hard coded as a master. When a value is written to the 'write byte' register, it will drive eight clocks and transfer a single byte in each direction. Bit 0 of the SPI status register will be set to 1 while the transfer is occurring. It will go back to zero when the transfer is finished. At this point, the program may read the 'SPI read byte' register.

Address r/w Environment Description
ffff00c0 w FEV SD SPI write byte
ffff00c4 r FEV SD SPI read byte
ffff00c8 r FEV SD SPI status (bit 0: ready)
ffff00cc w FEV SD SPI control (bit 0: chip select)
ffff00d0 w F V SD clock divider

GPIO Mode

Address r/w Environment Description
ffff00c0 w F SD GPIO direction
ffff00c4 w F SD GPIO value

SD GPIO pins map to the following direction/value register bits:

Bit Connection
0 dat[0]
1 dat[1]
2 dat[2]
3 dat[3]
4 cmd
5 clk

Loopback UART

The loopback UART has its transmit and receive signals connected. It's used by UART unit tests and only exists in the Verilator environment.

Address r/w Environment Description
ffff0140 r V Loopback UART Status
ffff0144 r V Loopback UART read
ffff0148 w V Loopback UART write
ffff014c w V Loopback UART divider

VGA Controller

The VGA controller is hardcoded for 32 bits per pixel.

Address r/w Environment Description
ffff0180 w FE VGA sequencer enable
ffff0184 w F VGA microcode write
ffff0188 w FE VGA frame buffer base address
ffff018c w F VGA frame buffer length (total pixels)

The timing signals are generated by a programmable timing generator that executes microprograms. It executes one instruction per pixel clock, which is hard coded for 25 MHz. The microinstruction is formatted as follows:

+------+-------+----------------+----------+----------+----------+------------+
| op(1)|cntr(1)| immediate (13) | vsync(1) | hsync(1) | frame(1) | visible(1) |
+------+-------+----------------+----------+----------+----------+------------+
  • op: Operation. A 0 will load the immediate value into a counter. A 1 will decrement and test the given counter. If it is zero, it will branch to the instruction offset given in immediate field. Otherwise, it will fall through to the next instruction.
  • cntr: Which counter this instruction uses. There are two counters: horizontal and vertical, although the hardware doesn't care which counter is used for what.
  • immediate: For operation 0, the new counter value. For operation 1, the branch destination
  • vsync: Value of vertical sync output when this instruction is executed
  • hsync: Value of horizontal sync output when this instruction is executed
  • frame: Indicates new frame. This will jump back to the first instruction of the program and restart the DMA engine from the frame buffer base address
  • visible: If this is set, this will pull the next pixel out of the DMA FIFO and will configure the analog circuitry to output video signal (it will be at zero volts otherwise to give a valid reference signal)

The processor can load the microprogram into the VGA controller with the following sequence:

  1. Disable the sequencer by writing zero into the sequencer enable register.
  2. Write each instruction sequentially to the microcode write register. Each instruction is 18 bits, LSB aligned in the register word.
  3. Re-enable the sequencer by writing a 1 to the sequencer enable register.

In the emulator environment, the timing generator program is ignored. The frame buffer size is configured using command line parameters. The VGA frame buffer base address/length registers are still used to configure what gets displayed to the frame buffer window.

Programmable Interrupt Timer

The programmable interrupt timer is a one shot timer that raises an interrupt when a specified number of clocks have elapsed. The timer is started as a side effect of writing a non-zero value to the interval register.

Address r/w Environment Description
ffff0240 w F V Timer interval (clocks)