551 lines
16 KiB
Verilog
551 lines
16 KiB
Verilog
// femtorv32, a minimalistic RISC-V RV32I core
|
|
// (minus SYSTEM and FENCE that are not implemented)
|
|
//
|
|
// Bruno Levy, May-June 2020
|
|
//
|
|
// This file: the "System on Chip" that goes with femtorv32.
|
|
|
|
/*************************************************************************************/
|
|
|
|
`default_nettype none // Makes it easier to detect typos !
|
|
|
|
`include "femtosoc_config.v" // User configuration of processor and SOC.
|
|
|
|
`ifdef NRV_MINIRV32
|
|
`include "PROCESSOR/mini_femtorv32.v" // Minimalistic version of the processor
|
|
`else
|
|
`include "PROCESSOR/femtorv32.v" // The processor
|
|
`endif
|
|
|
|
`include "PLL/femtopll.v" // The PLL (generates clock at NRV_FREQ)
|
|
|
|
`include "DEVICES/HardwareConfig.v" // Constant registers to query hardware config.
|
|
`include "DEVICES/uart.v" // The UART (serial port over USB)
|
|
`include "DEVICES/SSD1351.v" // The OLED display
|
|
`include "DEVICES/SPIFlash.v" // Read data from the serial flash chip
|
|
`include "DEVICES/MappedSPIFlash.v" // Idem, but mapped in memory
|
|
`include "DEVICES/MAX7219.v" // 8x8 led matrix driven by a MAX7219 chip
|
|
`include "DEVICES/LEDs.v" // Driver for 4 leds
|
|
`include "DEVICES/SDCard.v" // Driver for SDCard (just for bitbanging for now)
|
|
`include "DEVICES/Buttons.v" // Driver for the buttons
|
|
`include "DEVICES/FGA.v" // Femto Graphic Adapter
|
|
|
|
/*************************************************************************************/
|
|
|
|
module femtosoc(
|
|
`ifdef NRV_IO_LEDS
|
|
`ifdef FOMU
|
|
output rgb0,rgb1,rgb2,
|
|
`else
|
|
output D1,D2,D3,D4,D5,
|
|
`endif
|
|
`endif
|
|
`ifdef NRV_IO_SSD1351
|
|
output oled_DIN, oled_CLK, oled_CS, oled_DC, oled_RST,
|
|
`endif
|
|
`ifdef NRV_IO_UART
|
|
input RXD,
|
|
output TXD,
|
|
`endif
|
|
`ifdef NRV_IO_MAX7219
|
|
output ledmtx_DIN, ledmtx_CS, ledmtx_CLK,
|
|
`endif
|
|
`ifdef NRV_SPI_FLASH
|
|
output spi_mosi, input spi_miso, output spi_cs_n,
|
|
`ifndef ULX3S
|
|
output spi_clk, // ULX3S has spi clk shared with ESP32, using USRMCLK (below)
|
|
`endif
|
|
`endif
|
|
`ifdef NRV_IO_SDCARD
|
|
output sd_mosi, input sd_miso, output sd_cs_n, output sd_clk,
|
|
`endif
|
|
`ifdef NRV_IO_BUTTONS
|
|
`ifdef ICE_FEATHER
|
|
input [3:0] buttons,
|
|
`else
|
|
input [5:0] buttons,
|
|
`endif
|
|
`endif
|
|
`ifdef ULX3S
|
|
output wifi_en,
|
|
`endif
|
|
input RESET,
|
|
`ifdef FOMU
|
|
output usb_dp, usb_dn, usb_dp_pu,
|
|
`endif
|
|
`ifdef NRV_IO_FGA
|
|
output [3:0] gpdi_dp,
|
|
`endif
|
|
input pclk
|
|
);
|
|
|
|
/********************* Technicalities **************************************/
|
|
|
|
// On the ULX3S, deactivate the ESP32 so that it does not interfere with
|
|
// the other devices (especially the SDCard).
|
|
`ifdef ULX3S
|
|
assign wifi_en = 1'b0;
|
|
`endif
|
|
|
|
// On the ULX3S, the CLK pin of the SPI is multiplexed with the ESP32.
|
|
// It can be accessed using the USRMCLK primitive of the ECP5
|
|
// as follows.
|
|
`ifdef NRV_SPI_FLASH
|
|
`ifdef ULX3S
|
|
wire spi_clk;
|
|
wire tristate = 1'b0;
|
|
USRMCLK u1 (.USRMCLKI(spi_clk), .USRMCLKTS(tristate));
|
|
`endif
|
|
`endif
|
|
|
|
`ifdef FOMU
|
|
// Internal wires for the LEDs,
|
|
// need to convert to signal for RGB led
|
|
wire D1,D2,D3,D4,D5;
|
|
// On the FOMU, USB pins should be statically driven if not used
|
|
assign usb_dp = 1'b0;
|
|
assign usb_dn = 1'b0;
|
|
assign usb_dp_pu = 1'b0;
|
|
`endif
|
|
|
|
wire clk;
|
|
|
|
femtoPLL #(
|
|
.freq(`NRV_FREQ)
|
|
) pll(
|
|
.pclk(pclk),
|
|
.clk(clk)
|
|
);
|
|
|
|
// A little delay for sending the reset signal after startup.
|
|
// Explanation here: (ice40 BRAM reads incorrect values during
|
|
// first cycles).
|
|
// http://svn.clifford.at/handicraft/2017/ice40bramdelay/README
|
|
// On the ICE40-UP5K, 4096 cycles do not suffice (-> 65536 cycles)
|
|
`ifdef ICE_STICK
|
|
reg [11:0] reset_cnt = 0;
|
|
`else
|
|
reg [15:0] reset_cnt = 0;
|
|
`endif
|
|
wire reset = &reset_cnt;
|
|
|
|
/* verilator lint_off WIDTH */
|
|
`ifdef NRV_NEGATIVE_RESET
|
|
always @(posedge clk,negedge RESET) begin
|
|
if(!RESET) begin
|
|
reset_cnt <= 0;
|
|
end else begin
|
|
reset_cnt <= reset_cnt + !reset;
|
|
end
|
|
end
|
|
`else
|
|
always @(posedge clk,posedge RESET) begin
|
|
if(RESET) begin
|
|
reset_cnt <= 0;
|
|
end else begin
|
|
reset_cnt <= reset_cnt + !reset;
|
|
end
|
|
end
|
|
`endif
|
|
/* verilator lint_on WIDTH */
|
|
|
|
/***************************************************************************************************
|
|
/*
|
|
* Memory and memory interface
|
|
* memory map:
|
|
* address[21:2] RAM word address (4 Mb max).
|
|
* address[22] IO page (1-hot)
|
|
* address[23] SPI page (1-hot)
|
|
*/
|
|
|
|
// The memory bus.
|
|
wire [31:0] mem_address; // 24 bits are used internally. The two LSBs are ignored (using word addresses)
|
|
wire [3:0] mem_wmask; // mem write mask and strobe /write Legal values are 000,0001,0010,0100,1000,0011,1100,1111
|
|
wire [31:0] mem_rdata; // processor <- (mem and peripherals)
|
|
wire [31:0] mem_wdata; // processor -> (mem and peripherals)
|
|
wire mem_rstrb; // mem read strobe. Goes high to initiate memory write.
|
|
wire mem_rbusy; // processor <- (mem and peripherals). Stays high until a read transfer is finished.
|
|
wire mem_wbusy; // processor <- (mem and peripherals). Stays high until a write transfer is finished.
|
|
|
|
wire mem_wstrb = |mem_wmask; // mem write strobe, goes high to initiate memory write (deduced from wmask)
|
|
|
|
// IO bus.
|
|
`ifdef NRV_MAPPED_SPI_FLASH
|
|
wire mem_address_is_ram = (mem_address[23:22] == 2'b00);
|
|
wire mem_address_is_io = (mem_address[23:22] == 2'b01);
|
|
wire mem_address_is_spi_flash = (mem_address[23:22] == 2'b10);
|
|
wire mapped_spi_flash_rbusy;
|
|
wire [31:0] mapped_spi_flash_rdata;
|
|
MappedSPIFlash mapped_spi_flash(
|
|
.clk(clk),
|
|
.rstrb(mem_rstrb && mem_address_is_spi_flash),
|
|
.word_address(mem_address[19:2]),
|
|
.rdata(mapped_spi_flash_rdata),
|
|
.rbusy(mapped_spi_flash_rbusy),
|
|
.CLK(spi_clk),
|
|
.CS_N(spi_cs_n),
|
|
.MOSI(spi_mosi),
|
|
.MISO(spi_miso)
|
|
);
|
|
`else
|
|
wire mem_address_is_io = mem_address[22];
|
|
wire mem_address_is_ram = !mem_address[22];
|
|
`endif
|
|
|
|
reg [31:0] io_rdata;
|
|
wire [31:0] io_wdata = mem_wdata;
|
|
wire io_rstrb = mem_rstrb && mem_address_is_io;
|
|
wire io_wstrb = mem_wstrb && mem_address_is_io;
|
|
wire [19:0] io_word_address = mem_address[21:2]; // word offset in io page
|
|
wire io_rbusy;
|
|
wire io_wbusy;
|
|
|
|
assign mem_rbusy = io_rbusy
|
|
`ifdef NRV_MAPPED_SPI_FLASH
|
|
| mapped_spi_flash_rbusy
|
|
`endif
|
|
;
|
|
|
|
assign mem_wbusy = io_wbusy;
|
|
|
|
|
|
|
|
wire [19:0] ram_word_address = mem_address[21:2];
|
|
reg [31:0] RAM[(`NRV_RAM/4)-1:0];
|
|
reg [31:0] ram_rdata;
|
|
|
|
// Initialize the RAM with the generated firmware hex file.
|
|
// The hex file is generated by the bundled elf-2-verilog converter (see TOOLS/FIRMWARE_WORDS_SRC)
|
|
initial begin
|
|
$readmemh("FIRMWARE/firmware.hex",RAM);
|
|
end
|
|
|
|
`ifdef NRV_IO_FGA
|
|
wire mem_address_is_vram = mem_address[21];
|
|
`else
|
|
parameter mem_address_is_vram = 1'b0;
|
|
`endif
|
|
|
|
// The power of YOSYS: it infers SB_RAM40_4K BRAM primitives automatically ! (and recognizes
|
|
// masked writes, amazing ...)
|
|
/* verilator lint_off WIDTH */
|
|
always @(posedge clk) begin
|
|
if(mem_address_is_ram && !mem_address_is_vram) begin
|
|
if(mem_wmask[0]) RAM[ram_word_address][ 7:0 ] <= mem_wdata[ 7:0 ];
|
|
if(mem_wmask[1]) RAM[ram_word_address][15:8 ] <= mem_wdata[15:8 ];
|
|
if(mem_wmask[2]) RAM[ram_word_address][23:16] <= mem_wdata[23:16];
|
|
if(mem_wmask[3]) RAM[ram_word_address][31:24] <= mem_wdata[31:24];
|
|
end
|
|
ram_rdata <= RAM[ram_word_address];
|
|
end
|
|
/* verilator lint_on WIDTH */
|
|
|
|
`ifdef NRV_IO_FGA
|
|
FGA graphic_adapter(
|
|
.clk(clk),
|
|
.sel(mem_address_is_vram),
|
|
.mem_wmask(mem_wmask),
|
|
.mem_address(mem_address[16:0]),
|
|
.mem_wdata(mem_wdata),
|
|
.pixel_clk(pclk),
|
|
.gpdi_dp(gpdi_dp)
|
|
);
|
|
`endif
|
|
|
|
`ifdef NRV_MAPPED_SPI_FLASH
|
|
assign mem_rdata = mem_address_is_io ? io_rdata :
|
|
mem_address_is_ram ? ram_rdata :
|
|
mapped_spi_flash_rdata;
|
|
`else
|
|
assign mem_rdata = mem_address_is_io ? io_rdata : ram_rdata;
|
|
`endif
|
|
|
|
/***************************************************************************************************
|
|
/*
|
|
* Memory-mapped IO
|
|
* Mapped IO uses "one-hot" addressing, to make decoder
|
|
* simpler (saves a lot of LUTs), as in J1/swapforth,
|
|
* thanks to Matthias Koch(Mecrisp author) for the idea !
|
|
* The included files contains the symbolic constants that
|
|
* determine which device uses which bit.
|
|
*/
|
|
|
|
`include "DEVICES/HardwareConfig_bits.v"
|
|
|
|
/*
|
|
* Devices are components plugged to the IO memory bus.
|
|
* A few words follow in case you want to write your own devices:
|
|
*
|
|
* Each device has one or several register(s). Each register
|
|
* can be optionally read or/and written.
|
|
* - Each register is selected by a .sel_xxx signal (where xxx
|
|
* is the name of the register). With the 1-hot encoding that
|
|
* I'm using, .sel_xxx is systematically one of the bits of the
|
|
* IO word address (it is also possible to write a real
|
|
* address decoder, at the expense of eating-up a larger
|
|
* number of LUTs).
|
|
* - If the device requites wait cycles for writing and/or reading,
|
|
* it can have a .wbusy and/or .rbusy signal(s). All the .wbusy
|
|
* and .rbusy signals of all the devices are ORed at the end of
|
|
* this file to form the .io_rbusy and .io_wbusy signals.
|
|
* - If the device has read access, then it has a 32-bits .xxx_rdata
|
|
* signal, that returns 32'b0 if the device is not selected, or the
|
|
* read data otherwise. All the .xxx_rdata signals of all the devices
|
|
* are ORed at the end of this file to form the 32-bits io_rdata signal.
|
|
* - Finally, of course, each device is plugged to some pins of the FPGA,
|
|
* the corresponding signals are in capital letters.
|
|
*/
|
|
|
|
|
|
/*********************** Hardware configuration ************/
|
|
/*
|
|
* Two memory-mapped constant registers that make it easy for
|
|
* client code to query installed RAM and configured devices
|
|
* (this one does not use any pin, of course).
|
|
*/
|
|
`ifdef NRV_IO_HARDWARE_CONFIG
|
|
wire [31:0] hwconfig_rdata;
|
|
HardwareConfig hwconfig(
|
|
.clk(clk),
|
|
.sel_memory(io_word_address[IO_HW_CONFIG_RAM_bit]),
|
|
.sel_devices(io_word_address[IO_HW_CONFIG_DEVICES_bit]),
|
|
.sel_cpuinfo(io_word_address[IO_HW_CONFIG_CPUINFO_bit]),
|
|
.rdata(hwconfig_rdata)
|
|
);
|
|
`endif
|
|
|
|
/*********************** Four LEDs ************************/
|
|
`ifdef NRV_IO_LEDS
|
|
wire [31:0] leds_rdata;
|
|
LEDDriver leds(
|
|
.clk(clk),
|
|
.rstrb(io_rstrb),
|
|
.wstrb(io_wstrb),
|
|
.sel(io_word_address[IO_LEDS_bit]),
|
|
.wdata(io_wdata),
|
|
.rdata(leds_rdata),
|
|
.LED({D4,D3,D2,D1})
|
|
);
|
|
`endif
|
|
|
|
/********************** SSD1351 oled display ************************/
|
|
`ifdef NRV_IO_SSD1351
|
|
wire SSD1351_wbusy;
|
|
SSD1351 oled_display(
|
|
.clk(clk),
|
|
.wstrb(io_wstrb),
|
|
.sel_cntl(io_word_address[IO_SSD1351_CNTL_bit]),
|
|
.sel_cmd(io_word_address[IO_SSD1351_CMD_bit]),
|
|
.sel_dat(io_word_address[IO_SSD1351_DAT_bit]),
|
|
.wdata(io_wdata),
|
|
.wbusy(SSD1351_wbusy),
|
|
.DIN(oled_DIN),
|
|
.CLK(oled_CLK),
|
|
.CS(oled_CS),
|
|
.DC(oled_DC),
|
|
.RST(oled_RST)
|
|
);
|
|
`endif
|
|
|
|
/********************** UART ****************************************/
|
|
`ifdef NRV_IO_UART
|
|
wire [31:0] uart_rdata;
|
|
UART uart(
|
|
.clk(clk),
|
|
.rstrb(io_rstrb),
|
|
.wstrb(io_wstrb),
|
|
.sel_dat(io_word_address[IO_UART_DAT_bit]),
|
|
.sel_cntl(io_word_address[IO_UART_CNTL_bit]),
|
|
.wdata(io_wdata),
|
|
.rdata(uart_rdata),
|
|
.RXD(RXD),
|
|
.TXD(TXD)
|
|
);
|
|
`endif
|
|
|
|
/********** MAX7219 led matrix driver *******************************/
|
|
`ifdef NRV_IO_MAX7219
|
|
wire max7219_wbusy;
|
|
MAX7219 max7219(
|
|
.clk(clk),
|
|
.wstrb(io_wstrb),
|
|
.sel(io_word_address[IO_MAX7219_DAT_bit]),
|
|
.wdata(io_wdata),
|
|
.wbusy(max7219_wbusy),
|
|
.DIN(ledmtx_DIN),
|
|
.CS(ledmtx_CS),
|
|
.CLK(ledmtx_CLK)
|
|
);
|
|
`endif
|
|
|
|
/********************* SPI flash reader *****************************/
|
|
/*
|
|
* This one only has a .wbusy signal, going high while address is sent
|
|
* and while data is read, then data is latched, and we do not need
|
|
* a .rbusy signal.
|
|
*/
|
|
`ifdef NRV_IO_SPI_FLASH
|
|
wire spi_flash_wbusy;
|
|
wire [31:0] spi_flash_rdata;
|
|
SPIFlash spi_flash(
|
|
.clk(clk),
|
|
.rstrb(io_rstrb),
|
|
.wstrb(io_wstrb),
|
|
.sel(io_word_address[IO_SPI_FLASH_bit]),
|
|
.wdata(io_wdata),
|
|
.wbusy(spi_flash_wbusy),
|
|
.rdata(spi_flash_rdata),
|
|
.CLK(spi_clk),
|
|
.CS_N(spi_cs_n),
|
|
.MOSI(spi_mosi),
|
|
.MISO(spi_miso)
|
|
);
|
|
`endif
|
|
|
|
/********************* SPI SDCard *********************************/
|
|
/*
|
|
* This one has an output register directly wired to the CLK,MOSI,CS_N
|
|
* and an input register directly wired to MISO. The software driver
|
|
* implements the SPI protocol by bit-banging (see FIRMWARE/LIBFEMTORV32/spi_sd.c).
|
|
* One day I'll replace it with a hardware driver... if I have time !
|
|
*/
|
|
`ifdef NRV_IO_SDCARD
|
|
wire [31:0] sdcard_rdata;
|
|
SDCard sdcard(
|
|
.clk(clk),
|
|
.rstrb(io_rstrb),
|
|
.wstrb(io_wstrb),
|
|
.sel(io_word_address[IO_SDCARD_bit]),
|
|
.wdata(io_wdata),
|
|
.rdata(sdcard_rdata),
|
|
.CLK(sd_clk),
|
|
.MISO(sd_miso),
|
|
.MOSI(sd_mosi),
|
|
.CS_N(sd_cs_n)
|
|
);
|
|
`endif
|
|
|
|
/********************* Buttons *************************************/
|
|
/*
|
|
* Directly wired to the buttons.
|
|
*/
|
|
`ifdef NRV_IO_BUTTONS
|
|
wire [31:0] buttons_rdata;
|
|
Buttons buttons_driver(
|
|
.sel(io_word_address[IO_BUTTONS_bit]),
|
|
.rdata(buttons_rdata),
|
|
.BUTTONS(buttons)
|
|
);
|
|
`endif
|
|
|
|
/************** io_rdata, io_rbusy and io_wbusy signals *************/
|
|
|
|
/*
|
|
* io_rdata is latched. Not mandatory, but probably allow higher freq, to be tested.
|
|
*/
|
|
always @(posedge clk) begin
|
|
io_rdata <= 0
|
|
`ifdef NRV_IO_HARDWARE_CONFIG
|
|
| hwconfig_rdata
|
|
`endif
|
|
`ifdef NRV_IO_LEDS
|
|
| leds_rdata
|
|
`endif
|
|
`ifdef NRV_IO_UART
|
|
| uart_rdata
|
|
`endif
|
|
`ifdef NRV_IO_SPI_FLASH
|
|
| spi_flash_rdata
|
|
`endif
|
|
`ifdef NRV_IO_SDCARD
|
|
| sdcard_rdata
|
|
`endif
|
|
`ifdef NRV_IO_BUTTONS
|
|
| buttons_rdata
|
|
`endif
|
|
;
|
|
end
|
|
|
|
// For now, we got no device that has
|
|
// blocking reads (SPI flash blocks on
|
|
// write address and waits for read data).
|
|
assign io_rbusy = 0 ;
|
|
|
|
assign io_wbusy = 0
|
|
`ifdef NRV_IO_SSD1351
|
|
| SSD1351_wbusy
|
|
`endif
|
|
`ifdef NRV_IO_MAX7219
|
|
| max7219_wbusy
|
|
`endif
|
|
`ifdef NRV_IO_SPI_FLASH
|
|
| spi_flash_wbusy
|
|
`endif
|
|
;
|
|
|
|
/****************************************************************/
|
|
/* And last but not least, the processor */
|
|
|
|
wire error;
|
|
|
|
`ifdef NRV_RV32M
|
|
parameter RV32M = 1;
|
|
`else
|
|
parameter RV32M = 0;
|
|
`endif
|
|
|
|
`ifdef NRV_TWOSTAGE_SHIFTER
|
|
parameter TWOSTAGE_SHIFTER = 1;
|
|
`else
|
|
parameter TWOSTAGE_SHIFTER = 0;
|
|
`endif
|
|
|
|
`ifdef NRV_LATCH_ALU
|
|
parameter LATCH_ALU = 1;
|
|
`else
|
|
parameter LATCH_ALU = 0;
|
|
`endif
|
|
|
|
FemtoRV32 #(
|
|
.ADDR_WIDTH(24)
|
|
`ifndef NRV_MINIRV32
|
|
,.RV32M(RV32M)
|
|
,.TWOSTAGE_SHIFTER(TWOSTAGE_SHIFTER)
|
|
,.LATCH_ALU(LATCH_ALU)
|
|
`endif
|
|
) processor(
|
|
.clk(clk),
|
|
.mem_addr(mem_address),
|
|
.mem_wdata(mem_wdata),
|
|
.mem_wmask(mem_wmask),
|
|
.mem_rdata(mem_rdata),
|
|
.mem_rstrb(mem_rstrb),
|
|
.mem_rbusy(mem_rbusy),
|
|
.mem_wbusy(mem_wbusy),
|
|
.reset(reset),
|
|
.error(error)
|
|
);
|
|
|
|
`ifdef NRV_IO_LEDS
|
|
assign D5 = error;
|
|
`ifdef FOMU
|
|
SB_RGBA_DRV #(
|
|
.CURRENT_MODE("0b1"), // half current
|
|
.RGB0_CURRENT("0b000011"), // 4 mA
|
|
.RGB1_CURRENT("0b000011"), // 4 mA
|
|
.RGB2_CURRENT("0b000011") // 4 mA
|
|
) RGBA_DRIVER (
|
|
.CURREN(1'b1),
|
|
.RGBLEDEN(1'b1),
|
|
.RGB0PWM(D1),
|
|
.RGB1PWM(D2),
|
|
.RGB2PWM(D3),
|
|
.RGB0(rgb0),
|
|
.RGB1(rgb1),
|
|
.RGB2(rgb2)
|
|
);
|
|
`endif
|
|
`endif
|
|
|
|
endmodule
|