RISC-V Steel Microcontroller Unit
Documentation
Architectural overview
RISC-V Steel Microcontroller Unit is the top hardware module of RISC-V Steel. It contains an instance of RISC-V Steel Processor Core, the main component of its design, and an instance of RISC-V Steel UART, GPIO, SPI, timer and memory modules.
The processor core is integrated to the memory, timer, UART, GPIO and SPI modules via a shared bus that coordinates memory accesses.
Source files
The Verilog source files of RISC-V Steel Microcontroller Unit are saved in the hardware/mcu/
folder of RISC-V Steel repository.
Instantiation template
rvsteel_mcu #(
// See Configuration paramaters below for more information.
.CLOCK_FREQUENCY (50000000 ),
.UART_BAUD_RATE (9600 ),
.MEMORY_SIZE (8192 ),
.MEMORY_INIT_FILE ("mem_init.hex" ),
.BOOT_ADDRESS (32'h00000000 ),
.GPIO_WIDTH (1 ),
.SPI_NUM_CHIP_SELECT (1 ))
rvsteel_mcu_instance (
// See I/O signals below for more information.
.clock (),
.reset (),
.halt (),
.uart_rx (),
.uart_tx (),
.gpio_input (),
.gpio_oe (),
.gpio_output (),
.sclk (),
.pico (),
.poci (),
.cs ());
Configuration parameters
Parameter name and description | Value type | Default value |
---|---|---|
BOOT_ADDRESSMemory address of the first instruction to be fetched and executed. | 32-bit hexadecimal | 32'h00000000 |
CLOCK_FREQUENCYFrequency (in Hertz) of the clock signal. |
Integer | 50000000 |
UART_BAUD_RATEBaud rate of the UART module (in bauds per second). | Integer | 9600 |
MEMORY_SIZESize of the Memory module (in bytes). | Integer | 8192 |
MEMORY_INIT_FILEAbsolute path to the memory initialization file. | String | (empty string) |
GPIO_WIDTHNumber of general-purpose I/O pins. | Integer | 1 |
SPI_NUM_CHIP_SELECTNumber of Chip Select (CS) lines for the SPI controller. | Integer | 1 |
I/O signals
Pin name and description | Direction | Size |
---|---|---|
clockClock input. | Input | 1 bit |
resetReset pin (active-high). | Input | 1 bit |
haltHalt pin (active-high). | Input | 1 bit |
uart_rxUART receiver pin. | Input | 1 bit |
uart_txUART transmitter pin. | Output | 1 bit |
gpio_inputGPIO input signals. | Input | GPIO_WIDTH |
gpio_oeGPIO output enable. | Output | GPIO_WIDTH |
gpio_outputGPIO output signals. | Output | GPIO_WIDTH |
sclkSPI Controller clock. | Output | 1 bit |
picoSPI Peripheral In Controller Out. | Output | 1 bit |
pociSPI Peripheral Out Controller In. | Input | 1 bit |
csSPI Chip Select lines. | Output | SPI_NUM_CHIP_SELECT |
Memory Map
Start address | Final address | Block size (Bytes) | Device |
---|---|---|---|
0x00000000 |
0x(MEMORY_SIZE-1) |
MEMORY_SIZE |
RAM |
0x80000000 |
0x8000000f |
16 | UART |
0x80010000 |
0x8001001f |
32 | Timer |
0x80020000 |
0x8002001f |
32 | GPIO |
0x80030000 |
0x8003001f |
32 | SPI |
Adding new devices
1. Open rvsteel_mcu.v
and look for the system bus configuration
// System bus configuration
localparam NUM_DEVICES = 5;
localparam D0_RAM = 0;
localparam D1_UART = 1;
localparam D2_MTIMER = 2;
localparam D3_GPIO = 3;
localparam D4_SPI = 4;
2. Increment the NUM_DEVICES
parameter value
The NUM_DEVICES
parameter sets the total number of devices and is used to size the width of the system bus.
3. Create a parameter holding the index that will be used to address the new device
The system bus configuration should look as follows:
localparam NUM_DEVICES = 6;
localparam D0_RAM = 0;
localparam D1_UART = 1;
localparam D2_MTIMER = 2;
localparam D3_GPIO = 3;
localparam D4_SPI = 4;
localparam D5_NEW_DEVICE = 5; // your new device
4. Assign the new device a memory region
You can assign the new device to any free region (see Memory Map). The region cannot overlap the address space of other devices and its size must be a power of 2.
In the example below, the new device D5_NEW_DEVICE
is assigned a 32KB region starting at 0x80040000
:
wire [NUM_DEVICES*32-1:0] device_start_address;
wire [NUM_DEVICES*32-1:0] device_region_size;
assign device_start_address [32*D0_RAM +: 32] = 32'h0000_0000;
assign device_region_size [32*D0_RAM +: 32] = MEMORY_SIZE;
assign device_start_address [32*D1_UART +: 32] = 32'h8000_0000;
assign device_region_size [32*D1_UART +: 32] = 16;
assign device_start_address [32*D2_MTIMER +: 32] = 32'h8001_0000;
assign device_region_size [32*D2_MTIMER +: 32] = 32;
assign device_start_address [32*D3_GPIO +: 32] = 32'h8002_0000;
assign device_region_size [32*D3_GPIO +: 32] = 32;
assign device_start_address [32*D4_SPI +: 32] = 32'h8003_0000;
assign device_region_size [32*D4_SPI +: 32] = 32;
// Your new device
assign device_start_address [32*D5_NEW_DEVICE+: 32] = 32'h8004_0000;
assign device_region_size [32*D5_NEW_DEVICE+: 32] = 32768;
5. Instantiate the new device
Finally, you have to instantiate the new device in the rvsteel_mcu
module and connect it to the system bus interface.
A template for instantiating and connecting the new device to the system bus is provided below:
// Instantiate the new device in the rvsteel_mcu.v module like this:
new_device
new_device_instance (
// I/O interface of the new device
.new_device_rw_address (device_rw_address ),
.new_device_read_data (device_read_data[32*D2_NEW_DEVICE +: 32] ),
.new_device_read_request (device_read_request[D2_NEW_DEVICE] ),
.new_device_read_response (device_read_response[D2_NEW_DEVICE] ),
.new_device_write_data (device_write_data ),
.new_device_write_strobe (device_write_strobe ),
.new_device_write_request (device_write_request[D2_NEW_DEVICE] ),
.new_device_write_response (device_write_response[D2_NEW_DEVICE] )
);
Info
RISC-V Steel Processor Core will issue read and write requests to the new device as described in the I/O Operations section. The new device must comply with this protocol.