├── .gitignore ├── LICENSE ├── README.md ├── icebreaker.pcf ├── main.mk └── src ├── .gitignore ├── Makefile ├── bus_arb.v ├── bus_arb_tb.v ├── chip_select.v ├── config.vlt ├── firmware.c ├── gpio.v ├── ibus.v ├── ibus_tb.v ├── ice40up5k_spram.v ├── icebreaker_sections.lds ├── irq.v ├── irq_tb.v ├── ram.v ├── ram_arb.v ├── ram_arb_tb.v ├── reset.v ├── reset_tb.v ├── serv.v ├── serv_tb.v ├── sim.v ├── soc.c ├── soc.h ├── spi.v ├── spi_tb.v ├── start.s ├── tb.v ├── timer.v ├── timer_tb.v ├── uart.v └── uart_tb.v /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*~ 3 | *.swp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2020, Dave Berkeley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | SERV SoC 3 | ======== 4 | 5 | Please see my [blog page](https://www.rotwang.co.uk/projects/serv.html) for more information. 6 | 7 | SoC based on SERV, Olof Kindgren's bit-serial RISC-V processor. It provides Execute in Place (XiP) from Flash. This improves the usability of the CPU core, at the cost of added complexity and slower execution speed. 8 | 9 | Instead of fetching code from DP_RAM, the SoC has a SPI interface that reads the Flash chip. Instruction fetches from the processor are directed to the Flash. 10 | 11 | Written in Verilog, targetted initially at the 12 | [Icebreaker](https://1bitsquared.com/products/icebreaker) 13 | FPGA development board. 14 | 15 | SERV is the World's smallest RISC-V processor. See [github](https://github.com/olofk/serv) for more details. This clever design uses a bit-serial architecture and some cunning coding to shrink the footprint down to a few hundred LUTs. 16 | 17 | I wanted to add XiP to allow program data to be stored in flash rather than preloaded into DPRAM. This gives much more code space and faster turn around for software development - as it removes the need to re-synthesise on every C code change. I've also used SPRAM for CPU RAM. This frees up valuable DPRAM for use by whatever else you are building. 18 | 19 | SERV uses a stripped down 20 | [Wishbone](https://en.wikipedia.org/wiki/Wishbone_(computer_bus)) 21 | bus. 22 | 23 | ---- 24 | 25 | Make 26 | ---- 27 | 28 | To build the code you need to be able to build SERV. See the build instructions there. 29 | 30 | You need the risc-v **gcc** cross compiler tools. I used C Wolf's instructions 31 | [here](https://github.com/cliffordwolf/picorv32#building-a-pure-rv32i-toolchain). 32 | 33 | You will need also need the synthesis and place-and-route tools, **yosys**, **nextpnr-ice40**. 34 | 35 | For development I also use **verilator**, as a lint tool, **vvp** to run test scripts and **gtkwave** to view the resulting waveforms. 36 | 37 | You need to set the environment variable SERV to point at your copy of the SERV git repository. 38 | 39 | You will also need the **gcc** risc-v tool chain on your PATH. 40 | 41 | eg: 42 | 43 | export SERV=~/Desktop/serv 44 | export PATH=/opt/riscv32i/bin:$PATH 45 | 46 | You can then run 47 | 48 | make all 49 | 50 | To build and program just the C code, run 51 | 52 | make prog_firmware 53 | 54 | There is an asciinema of the process 55 | [here](https://asciinema.org/a/351518) 56 | 57 | I've added a 64-bit counter / timer, which is greedy in terms of resources. There is a also a simple Tx-only UART set to 115200 Baud. 58 | Plus an interrupt controller and simple GPIO. 59 | This should give the basics of what people need to build their own SoCs. 60 | 61 | I've added code to allow **malloc()** and **free()** to work, 62 | the linker script icebreaker_sections.lds is based on code in 63 | Claire Wolf's excellent 64 | [picosoc processor](https://github.com/cliffordwolf/picorv32/tree/master/picosoc). 65 | 66 | ---- 67 | 68 | TODO 69 | 70 | * use a higher clock speed to drive the Flash SPI interface. The chip can go up to 50MHz. This will improve the ibus fetch rate by up to 4 times. 71 | * use DSPI or QSPI to fetch data from the Flash. This could double or quadruple the ibus fetch speed. 72 | * Add DMA, to allow the host RAM space to be shared by other parts of the FPGA. 73 | 74 | ---- 75 | 76 | I'd like to thank Olof Kindgren and Claire Wolf for their amazing work. 77 | The availability of open source CPU cores open up a whole world of 78 | possibilities for anyone doing FPGA development. 79 | Reading their code and using their work has been 80 | both educational and inspirational. 81 | 82 | ---- 83 | 84 | -------------------------------------------------------------------------------- /icebreaker.pcf: -------------------------------------------------------------------------------- 1 | # 12 MHz clock 2 | set_io -nowarn CLK 35 3 | 4 | # RS232 5 | set_io -nowarn RX 6 6 | set_io -nowarn TX 9 7 | 8 | # LEDs and Button 9 | set_io -nowarn BTN_N 10 10 | set_io -nowarn LEDR_N 11 11 | set_io -nowarn LEDG_N 37 12 | 13 | # RGB LED Driver 14 | set_io -nowarn LED_RED_N 39 15 | set_io -nowarn LED_GRN_N 40 16 | set_io -nowarn LED_BLU_N 41 17 | 18 | # SPI Flash 19 | set_io -nowarn FLASH_SCK 15 20 | set_io -nowarn FLASH_SSB 16 21 | set_io -nowarn FLASH_IO0 14 22 | set_io -nowarn FLASH_IO1 17 23 | set_io -nowarn FLASH_IO2 12 24 | set_io -nowarn FLASH_IO3 13 25 | 26 | # PMOD 1A 27 | set_io -nowarn P1A1 4 28 | set_io -nowarn P1A2 2 29 | set_io -nowarn P1A3 47 30 | set_io -nowarn P1A4 45 31 | set_io -nowarn P1A7 3 32 | set_io -nowarn P1A8 48 33 | set_io -nowarn P1A9 46 34 | set_io -nowarn P1A10 44 35 | 36 | # PMOD 1B 37 | set_io -nowarn P1B1 43 38 | set_io -nowarn P1B2 38 39 | set_io -nowarn P1B3 34 40 | set_io -nowarn P1B4 31 41 | set_io -nowarn P1B7 42 42 | set_io -nowarn P1B8 36 43 | set_io -nowarn P1B9 32 44 | set_io -nowarn P1B10 28 45 | 46 | # PMOD 2 47 | set_io -nowarn P2_1 27 48 | set_io -nowarn P2_2 25 49 | set_io -nowarn P2_3 21 50 | set_io -nowarn P2_4 19 51 | set_io -nowarn P2_7 26 52 | set_io -nowarn P2_8 23 53 | set_io -nowarn P2_9 20 54 | set_io -nowarn P2_10 18 55 | 56 | # LEDs and Buttons (PMOD 2) 57 | set_io -nowarn LED1 26 58 | set_io -nowarn LED2 27 59 | set_io -nowarn LED3 25 60 | set_io -nowarn LED4 23 61 | set_io -nowarn LED5 21 62 | set_io -nowarn BTN1 20 63 | set_io -nowarn BTN2 19 64 | set_io -nowarn BTN3 18 65 | -------------------------------------------------------------------------------- /main.mk: -------------------------------------------------------------------------------- 1 | 2 | all: $(PROJ).rpt $(PROJ).bin 3 | 4 | %.blif: %.v $(ADD_SRC) $(ADD_DEPS) 5 | yosys -ql $*.log $(if $(USE_ARACHNEPNR),-DUSE_ARACHNEPNR) -p 'synth_ice40 -top top -blif $@' $< $(ADD_SRC) 6 | 7 | VERILATOR_SRC?=$(ADD_SRC) 8 | 9 | %.json: %.v $(ADD_SRC) $(ADD_DEPS) 10 | verilator --top-module top $(ADD_VERILATOR) $< $(VERILATOR_SRC) /usr/share/yosys/ice40/cells_sim.v --lint-only -Wall 11 | yosys -ql $*.log $(if $(USE_ARACHNEPNR),-DUSE_ARACHNEPNR) -p 'synth_ice40 $(ADD_YOSYS) -top top -json $@' $< $(ADD_SRC) 12 | 13 | ifeq ($(USE_ARACHNEPNR),) 14 | %.asc: $(PIN_DEF) %.json 15 | nextpnr-ice40 --$(DEVICE) $(if $(PACKAGE),--package $(PACKAGE)) $(if $(FREQ),--freq $(FREQ)) --json $(filter-out $<,$^) --pcf $< --asc $@ 16 | else 17 | %.asc: $(PIN_DEF) %.blif 18 | arachne-pnr -d $(subst up,,$(subst hx,,$(subst lp,,$(DEVICE)))) $(if $(PACKAGE),-P $(PACKAGE)) -o $@ -p $^ 19 | endif 20 | 21 | 22 | %.bin: %.asc 23 | icepack $< $@ 24 | 25 | %.rpt: %.asc 26 | icetime $(if $(FREQ),-c $(FREQ)) -d $(DEVICE) -mtr $@ $< 27 | 28 | %_tb: %_tb.v %.v $(TB_DEPS) 29 | verilator --top-module top $(ADD_VERILATOR) $(TB_VERILATOR) $(TB_SRC) $(PROJ).v --lint-only -Wall 30 | iverilog -DSIMULATION -g2012 $(ADD_IVERILOG) -o $@ $(TB_SRC) $@.v $(ADD_TB_IVERILOG) 31 | ./$@ 32 | #gtkwave $(PROJ).vcd $@.gtkw & 33 | 34 | %_tb.vcd: %_tb 35 | vvp -N $< +vcd=$@ 36 | 37 | %_syn.v: %.blif 38 | yosys -p 'read_blif -wideports $^; write_verilog $@' 39 | 40 | %_syntb: %_tb.v %_syn.v 41 | verilator --top-module top $< $(ADD_SRC) --lint-only -Wall -Wno-DECLFILENAME 42 | iverilog -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` 43 | 44 | %_syntb.vcd: %_syntb 45 | vvp -N $< +vcd=$@ 46 | 47 | prog: $(PROJ).bin 48 | iceprog $< 49 | 50 | clean: 51 | rm -f $(PROJ).blif $(PROJ).asc $(PROJ).rpt $(PROJ).bin $(PROJ).json $(PROJ).log *.vvp *.vcd $(ADD_CLEAN) *~ 52 | 53 | .SECONDARY: 54 | .PHONY: all prog clean 55 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | firmware.bin 2 | firmware.elf 3 | pll.v 4 | serv.asc 5 | serv.bin 6 | serv.json 7 | serv.log 8 | serv.rpt 9 | serv.vcd 10 | *_tb 11 | *.gtkw 12 | source.sh 13 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CROSS=riscv32-unknown-elf- 3 | 4 | ifndef SERV 5 | S = https://github.com/olofk/serv 6 | $(error SERV is not set, see $(S)) 7 | endif 8 | 9 | CORE=$(SERV)/rtl 10 | 11 | PROJ = serv 12 | ADD_SRC = 13 | ADD_CLEAN = pll.v firmware.elf firmware.bin $(PROJ).bin *_tb $(VCONFIG) 14 | ADD_YOSYS = -dsp 15 | ADD_VERILATOR = $(VCONFIG) +incdir+$(CORE) 16 | ADD_DEPS = pll.v $(VCONFIG) 17 | ADD_IVERILOG = -I $(CORE) 18 | TB_DEPS = pll.v $(VCONFIG) 19 | TB_VERILATOR = /usr/share/yosys/ice40/cells_sim.v sim.v 20 | ADD_TB_IVERILOG = sim.v tb.v 21 | 22 | VCONFIG = /tmp/$(PROJ)_config.vlt 23 | 24 | PACKAGE = sg48 25 | DEVICE = up5k 26 | 27 | PIN_DEF = ../icebreaker.pcf 28 | 29 | FILES = pll.v 30 | FILES += reset.v 31 | FILES += ibus.v 32 | FILES += uart.v 33 | FILES += spi.v 34 | FILES += gpio.v 35 | FILES += chip_select.v 36 | FILES += ram_arb.v 37 | FILES += bus_arb.v 38 | FILES += timer.v 39 | FILES += irq.v 40 | 41 | HW_FILES = ice40up5k_spram.v 42 | HW_FILES += ram.v 43 | 44 | # CPU internals 45 | CPU_FILES = $(CORE)/serv_rf_top.v 46 | CPU_FILES += $(CORE)/serv_rf_ram_if.v 47 | CPU_FILES += $(CORE)/serv_rf_ram.v 48 | CPU_FILES += $(CORE)/serv_rf_if.v 49 | CPU_FILES += $(CORE)/serv_mem_if.v 50 | CPU_FILES += $(CORE)/serv_top.v 51 | CPU_FILES += $(CORE)/serv_state.v 52 | CPU_FILES += $(CORE)/serv_decode.v 53 | CPU_FILES += $(CORE)/serv_bufreg.v 54 | CPU_FILES += $(CORE)/serv_ctrl.v 55 | CPU_FILES += $(CORE)/serv_alu.v 56 | CPU_FILES += $(CORE)/serv_shift.v 57 | CPU_FILES += $(CORE)/serv_csr.v 58 | 59 | ADD_SRC+=$(FILES) $(CPU_FILES) $(HW_FILES) 60 | TB_SRC = $(CPU_FILES) $(FILES) 61 | 62 | 63 | include ../main.mk 64 | 65 | prog_firmware: firmware.bin 66 | iceprog -o 1M $^ 67 | 68 | all: prog_firmware prog 69 | 70 | pll.v : Makefile 71 | icepll -o 32 -m -f $@ 72 | # Change *_CORE macro to *_PAD macro 73 | # See SiliconBlue ICE TM Technology Library Version 2.3 74 | sed -i 's/SB_PLL40_CORE/SB_PLL40_PAD/g' $@ 75 | sed -i 's/.REFERENCECLK/.PACKAGEPIN/g' $@ 76 | 77 | firmware.bin: firmware.elf 78 | $(CROSS)objcopy -S -O binary $^ $@ 79 | 80 | CFLAGS = -DICEBREAKER 81 | CFLAGS += -march=rv32i 82 | CFLAGS += -ffreestanding 83 | CFLAGS += -nostartfiles 84 | CFLAGS += -Wall -Werror 85 | CFLAGS += -O1 86 | LFLAGS = -Wl,-Bstatic,-T,icebreaker_sections.lds,--strip-debug 87 | 88 | firmware.elf: firmware.c start.s soc.c Makefile icebreaker_sections.lds 89 | $(CROSS)g++ $(CFLAGS) $(LFLAGS) -o $@ start.s firmware.c soc.c 90 | 91 | $(VCONFIG): config.vlt 92 | envsubst < $^ > $@ 93 | 94 | tb: serv_tb bus_arb_tb ibus_tb uart_tb reset_tb spi_tb timer_tb irq_tb ram_arb_tb 95 | 96 | # FIN 97 | -------------------------------------------------------------------------------- /src/bus_arb.v: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * iBus Arbitration 4 | */ 5 | 6 | module bus_arb( 7 | input wire wb_clk, 8 | // Device A 9 | input wire a_cyc, 10 | input wire [31:0] a_adr, 11 | output wire a_ack, 12 | output wire [31:0] a_rdt, 13 | // Device B 14 | input wire b_cyc, 15 | input wire [31:0] b_adr, 16 | output wire b_ack, 17 | output wire [31:0] b_rdt, 18 | // Controlled Device 19 | output wire x_cyc, 20 | output wire [31:0] x_adr, 21 | input wire x_ack, 22 | input wire [31:0] x_rdt, 23 | output wire busy 24 | ); 25 | 26 | /* verilator lint_off UNUSED */ 27 | wire x_we; 28 | wire [3:0] x_sel; 29 | wire [31:0] x_dat; 30 | /* verilator lint_on UNUSED */ 31 | 32 | ram_arb #(.WIDTH(32)) 33 | arb 34 | ( 35 | .wb_clk(wb_clk), 36 | .a_cyc(a_cyc), 37 | .a_adr(a_adr), 38 | .a_ack(a_ack), 39 | .a_rdt(a_rdt), 40 | .b_cyc(b_cyc), 41 | .b_adr(b_adr), 42 | .b_ack(b_ack), 43 | .b_rdt(b_rdt), 44 | .x_cyc(x_cyc), 45 | .x_adr(x_adr), 46 | .x_ack(x_ack), 47 | .x_rdt(x_rdt), 48 | // signals not present in ROM 49 | .a_we(1'b0), 50 | .a_sel(4'b0), 51 | .a_dat(32'b0), 52 | .b_we(1'b0), 53 | .b_sel(4'b0), 54 | .b_dat(32'b0), 55 | .x_we(x_we), 56 | .x_sel(x_sel), 57 | .x_dat(x_dat) 58 | ); 59 | 60 | assign busy = x_cyc; 61 | 62 | endmodule 63 | 64 | -------------------------------------------------------------------------------- /src/bus_arb_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top (); 10 | 11 | reg wb_clk = 0; 12 | 13 | always #7 wb_clk <= !wb_clk; 14 | 15 | reg wb_rst = 1; 16 | 17 | initial begin 18 | $dumpfile("serv.vcd"); 19 | $dumpvars(0, top); 20 | 21 | @(posedge wb_clk); 22 | @(posedge wb_clk); 23 | 24 | wb_rst <= 0; 25 | end 26 | 27 | // iBus arbitration 28 | 29 | reg wb_ibus_cyc = 0; 30 | reg [31:0] wb_ibus_adr = 0; 31 | wire wb_ibus_ack; 32 | wire [31:0] wb_ibus_rdt; 33 | 34 | reg f_cyc = 0; 35 | reg [31:0] f_adr = 0; 36 | wire f_ack; 37 | wire [31:0] f_rdt; 38 | 39 | wire s_cyc; 40 | wire [31:0] s_adr; 41 | reg s_ack = 0; 42 | reg [31:0] s_rdt = 0; 43 | 44 | wire busy; 45 | 46 | bus_arb ibus_arb( 47 | .wb_clk(wb_clk), 48 | // CPU is the priority channel 49 | .a_cyc(wb_ibus_cyc), 50 | .a_adr(wb_ibus_adr), 51 | .a_ack(wb_ibus_ack), 52 | .a_rdt(wb_ibus_rdt), 53 | // Flash_read at a lower priority 54 | .b_cyc(f_cyc), 55 | .b_adr(f_adr), 56 | .b_ack(f_ack), 57 | .b_rdt(f_rdt), 58 | // Connect to the ibus SPI controller 59 | .x_cyc(s_cyc), 60 | .x_adr(s_adr), 61 | .x_ack(s_ack), 62 | .x_rdt(s_rdt), 63 | .busy(busy) 64 | ); 65 | 66 | // TODO : simulate slow device 67 | always @(posedge wb_clk) begin 68 | if (s_cyc) begin 69 | s_ack <= 1; 70 | s_rdt <= s_adr; 71 | end 72 | if (s_ack) begin 73 | s_ack <= 0; 74 | s_rdt <= 0; 75 | end 76 | end 77 | 78 | reg [31:0] ibus_data = 32'hZ; 79 | reg ibus_ready = 0; 80 | reg [31:0] fbus_data = 32'hZ; 81 | reg fbus_ready = 0; 82 | 83 | // Latch data. 84 | // Clear *_cyc on *_ack 85 | 86 | always @(posedge wb_clk) begin 87 | 88 | if (f_ack) begin 89 | f_cyc <= 0; 90 | f_adr <= 32'hZ; 91 | // latch the data 92 | fbus_data <= f_rdt; 93 | fbus_ready <= 1; 94 | end 95 | 96 | if (wb_ibus_ack) begin 97 | wb_ibus_cyc <= 0; 98 | wb_ibus_adr <= 32'hZ; 99 | // latch the data 100 | ibus_data <= wb_ibus_rdt; 101 | ibus_ready <= 1; 102 | end 103 | 104 | end 105 | 106 | task ifetch(input [31:0] addr); 107 | 108 | begin 109 | // Request ibus fetch 110 | wb_ibus_cyc <= 1; 111 | wb_ibus_adr <= addr; 112 | ibus_data <= 32'hZ; 113 | ibus_ready <= 0; 114 | end 115 | 116 | endtask 117 | 118 | task ffetch(input [31:0] addr); 119 | 120 | begin 121 | // Request fbus fetch 122 | f_cyc <= 1; 123 | f_adr <= addr; 124 | fbus_data <= 32'hZ; 125 | fbus_ready <= 0; 126 | end 127 | 128 | endtask 129 | 130 | task die; 131 | 132 | begin 133 | @(posedge wb_clk); 134 | @(posedge wb_clk); 135 | @(posedge wb_clk); 136 | @(posedge wb_clk); 137 | @(posedge wb_clk); 138 | @(posedge wb_clk); 139 | @(posedge wb_clk); 140 | @(posedge wb_clk); 141 | @(posedge wb_clk); 142 | $finish; 143 | end 144 | 145 | endtask 146 | 147 | initial begin 148 | wait(!wb_rst); 149 | @(posedge wb_clk); 150 | 151 | // fetch in ibus 152 | ifetch(32'h100000); 153 | wait(ibus_ready); 154 | tb_assert(ibus_data == 32'h100000); 155 | 156 | wait(!busy); 157 | 158 | // fetch on fbus 159 | ffetch(32'h123456); 160 | wait(fbus_ready); 161 | tb_assert(fbus_data == 32'h123456); 162 | 163 | wait(!busy); 164 | 165 | // fetch both simultaneous : A should go first 166 | ifetch(32'hfaceface); 167 | ffetch(32'hcafecafe); 168 | @(posedge wb_clk); 169 | 170 | wait(ibus_ready); 171 | tb_assert(ibus_data == 32'hfaceface); 172 | wait(fbus_ready); 173 | //tb_assert(fbus_data == 32'hcafecafe); 174 | @(posedge wb_clk); 175 | 176 | wait(!busy); 177 | 178 | // start A, then make B req while busy 179 | ifetch(32'h34343434); 180 | @(posedge wb_clk); 181 | ffetch(32'h34563456); 182 | @(posedge wb_clk); 183 | wait(ibus_ready); 184 | tb_assert(ibus_data == 32'h34343434); 185 | wait(fbus_ready); 186 | tb_assert(fbus_data == 32'h34563456); 187 | @(posedge wb_clk); 188 | 189 | wait(!busy); 190 | 191 | // start B, then make A req while busy 192 | ffetch(32'hcafecafe); 193 | @(posedge wb_clk); 194 | ifetch(32'hfaceface); 195 | @(posedge wb_clk); 196 | wait(fbus_ready); 197 | tb_assert(fbus_data == 32'hcafecafe); 198 | wait(ibus_ready); 199 | tb_assert(ibus_data == 32'hfaceface); 200 | @(posedge wb_clk); 201 | 202 | wait(!busy); 203 | 204 | // Try different staggering of start A .. B 205 | 206 | // A ck ck ck B 207 | ifetch(32'haaaaaaa1); 208 | @(posedge wb_clk); 209 | @(posedge wb_clk); 210 | @(posedge wb_clk); 211 | ffetch(32'hbbbbbbb1); 212 | @(posedge wb_clk); 213 | 214 | wait(fbus_ready); 215 | tb_assert(fbus_data == 32'hbbbbbbb1); 216 | wait(ibus_ready); 217 | tb_assert(ibus_data == 32'haaaaaaa1); 218 | @(posedge wb_clk); 219 | 220 | wait(!busy); 221 | 222 | // A ck ck B 223 | ifetch(32'haaaaaaa2); 224 | @(posedge wb_clk); 225 | @(posedge wb_clk); 226 | ffetch(32'hbbbbbbb2); 227 | @(posedge wb_clk); 228 | 229 | wait(fbus_ready); 230 | tb_assert(fbus_data == 32'hbbbbbbb2); 231 | wait(ibus_ready); 232 | tb_assert(ibus_data == 32'haaaaaaa2); 233 | @(posedge wb_clk); 234 | 235 | wait(!busy); 236 | 237 | // A ck B 238 | ifetch(32'haaaaaaa3); 239 | @(posedge wb_clk); 240 | ffetch(32'hbbbbbbb3); 241 | @(posedge wb_clk); 242 | 243 | wait(fbus_ready); 244 | tb_assert(fbus_data == 32'hbbbbbbb3); 245 | wait(ibus_ready); 246 | tb_assert(ibus_data == 32'haaaaaaa3); 247 | @(posedge wb_clk); 248 | 249 | wait(!busy); 250 | 251 | // A & B 252 | ifetch(32'haaaaaaa4); 253 | ffetch(32'hbbbbbbb4); 254 | @(posedge wb_clk); 255 | 256 | wait(fbus_ready); 257 | tb_assert(fbus_data == 32'hbbbbbbb4); 258 | wait(ibus_ready); 259 | tb_assert(ibus_data == 32'haaaaaaa4); 260 | @(posedge wb_clk); 261 | 262 | wait(!busy); 263 | 264 | // B ck A 265 | ffetch(32'hbbbbbbb5); 266 | @(posedge wb_clk); 267 | ifetch(32'haaaaaaa5); 268 | @(posedge wb_clk); 269 | 270 | wait(fbus_ready); 271 | tb_assert(fbus_data == 32'hbbbbbbb5); 272 | wait(ibus_ready); 273 | tb_assert(ibus_data == 32'haaaaaaa5); 274 | @(posedge wb_clk); 275 | 276 | wait(!busy); 277 | 278 | // B ck ck A 279 | ffetch(32'hbbbbbbb6); 280 | @(posedge wb_clk); 281 | @(posedge wb_clk); 282 | ifetch(32'haaaaaaa6); 283 | @(posedge wb_clk); 284 | 285 | wait(fbus_ready); 286 | tb_assert(fbus_data == 32'hbbbbbbb6); 287 | wait(ibus_ready); 288 | tb_assert(ibus_data == 32'haaaaaaa6); 289 | @(posedge wb_clk); 290 | 291 | wait(!busy); 292 | 293 | // B ck ck ck A 294 | ffetch(32'hbbbbbbb7); 295 | @(posedge wb_clk); 296 | @(posedge wb_clk); 297 | @(posedge wb_clk); 298 | ifetch(32'haaaaaaa7); 299 | @(posedge wb_clk); 300 | 301 | wait(fbus_ready); 302 | tb_assert(fbus_data == 32'hbbbbbbb7); 303 | wait(ibus_ready); 304 | tb_assert(ibus_data == 32'haaaaaaa7); 305 | @(posedge wb_clk); 306 | 307 | wait(!busy); 308 | 309 | @(posedge wb_clk); 310 | @(posedge wb_clk); 311 | @(posedge wb_clk); 312 | @(posedge wb_clk); 313 | @(posedge wb_clk); 314 | @(posedge wb_clk); 315 | @(posedge wb_clk); 316 | @(posedge wb_clk); 317 | @(posedge wb_clk); 318 | $finish; 319 | end 320 | 321 | endmodule 322 | 323 | -------------------------------------------------------------------------------- /src/chip_select.v: -------------------------------------------------------------------------------- 1 | 2 | module chip_select 3 | #(parameter ADDR=0, WIDTH=8) 4 | (input wire wb_ck, 5 | input wire [(WIDTH-1):0] addr, 6 | input wire wb_cyc, 7 | input wire wb_rst, 8 | output wire ack, 9 | output wire cyc); 10 | 11 | wire match; 12 | assign match = addr == ADDR; 13 | assign cyc = match & wb_cyc; 14 | 15 | reg [1:0] state = 0; 16 | 17 | always @(posedge wb_ck) begin 18 | 19 | if (wb_rst || (!match) || !wb_cyc) 20 | state <= 0; 21 | else begin 22 | case (state) 23 | 0 : state <= 1; 24 | 1 : state <= 2; 25 | 2 : state <= 3; 26 | 3 : state <= 0; 27 | endcase 28 | end 29 | 30 | end 31 | 32 | assign ack = state == 1; 33 | 34 | endmodule 35 | 36 | -------------------------------------------------------------------------------- /src/config.vlt: -------------------------------------------------------------------------------- 1 | `verilator_config 2 | lint_off -file "pll.v" 3 | lint_off -file "ice40up5k_spram.v" 4 | lint_off -file "/usr/share/yosys/ice40/cells_sim.v" 5 | lint_off -file "${SERV}/rtl/serv_top.v" 6 | lint_off -file "${SERV}/rtl/serv_mem_if.v" 7 | lint_off -file "${SERV}/rtl/serv_ctrl.v" 8 | lint_off -file "${SERV}/rtl/serv_alu.v" 9 | lint_off -rule DECLFILENAME 10 | -------------------------------------------------------------------------------- /src/firmware.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "soc.h" 9 | 10 | #define LEDS ((uint32_t volatile*) 0x40000000) 11 | #define flash ((uint32_t volatile*) 0x70000000) 12 | 13 | // Memory locations defined in the linker config. 14 | extern "C" uint32_t _stext, _etext, _sdata, _edata, _sheap, _eheap, _sstack, _estack, _ivector; 15 | 16 | /* 17 | * 18 | */ 19 | 20 | extern "C" void irq_handler(void)__attribute__((interrupt));; 21 | 22 | void irq_handler(void) 23 | { 24 | // check for timer interrupt 25 | uint32_t cause = read_mcause(); 26 | if ((cause & 0xff) != 0x07) 27 | { 28 | return; 29 | } 30 | 31 | static uint64_t s = 0x01000000; 32 | 33 | s += 0x01000000; 34 | timer_set(s); 35 | 36 | static int i = 0; 37 | LEDS[0] = i++; 38 | } 39 | 40 | // banner made with : figlet "SERV Risc-V" | sed 's/\\/\\\\/g' 41 | char banner[] = 42 | "\r\n" 43 | " ____ _____ ______ __ ____ _ __ __\r\n" 44 | " / ___|| ____| _ \\ \\ / / | _ \\(_)___ ___ \\ \\ / /\r\n" 45 | " \\___ \\| _| | |_) \\ \\ / / | |_) | / __|/ __|___\\ \\ / / \r\n" 46 | " ___) | |___| _ < \\ V / | _ <| \\__ \\ (_|_____\\ V / \r\n" 47 | " |____/|_____|_| \\_\\ \\_/ |_| \\_\\_|___/\\___| \\_/ \r\n" 48 | "\r\n" 49 | "The World's smallest RISC-V CPU. Using Bit-serial Architecture.\r\n" 50 | "\r\n" 51 | "https://github.com/olofk/serv\r\n\r\n"; 52 | 53 | /* 54 | * 55 | */ 56 | 57 | void show_section(const char* text, uint32_t *start, uint32_t *end) 58 | { 59 | uint32_t s = (uint32_t) start; 60 | uint32_t e = (uint32_t) end; 61 | 62 | print(text); 63 | print(" addr 0x"); 64 | print_num(s, 16, 6); 65 | print(" size 0x"); 66 | print_num(e - s, 16, 6); 67 | print("\r\n"); 68 | } 69 | 70 | /* 71 | * 72 | */ 73 | 74 | int main(void) 75 | { 76 | *LEDS = 0; 77 | 78 | #if 1 79 | print(banner); 80 | 81 | print("RAM "); 82 | print_num((uint32_t) &_estack, 10, 6); 83 | print(" bytes\r\n"); 84 | print("\r\n"); 85 | show_section("Program :", & _stext, & _etext); 86 | show_section("Data :", & _sdata, & _edata); 87 | show_section("Heap :", & _sheap, & _eheap); 88 | show_section("Stack :", & _sstack, & _estack); 89 | print("\r\n"); 90 | #endif 91 | 92 | while (true) ; 93 | 94 | uint32_t v; 95 | 96 | timer_set(0x01000000); 97 | 98 | uint64_t t = timer_get(); 99 | print("Timer 0x"); 100 | print_num(t >> 32, 16, 8); 101 | print_num(t & 0xFFFFFFFF, 16, 8); 102 | print("\r\n"); 103 | print("Compare 0x"); 104 | t = timer_get_cmp(); 105 | print_num(t >> 32, 16, 8); 106 | print_num(t & 0xFFFFFFFF, 16, 8); 107 | print("\r\n"); 108 | 109 | // This instruction does not work! 110 | write_mie(0x08); 111 | 112 | write_mstatus(0x8); 113 | write_mtvec((uint32_t) irq_handler); 114 | 115 | int j = 0; 116 | 117 | while (true) 118 | { 119 | t = timer_get(); 120 | print_num(t >> 32, 16, 8); 121 | print("_"); 122 | print_num(t & 0xFFFFFFFF, 16, 8); 123 | 124 | print(" E:"); 125 | v = read_mie(); 126 | print_num(v, 16, 8); 127 | 128 | print(" S:"); 129 | v = read_mstatus(); 130 | print_num(v, 16, 8); 131 | 132 | print(" C:"); 133 | v = read_mcause(); 134 | print_num(v, 16, 8); 135 | 136 | print(" "); 137 | t = timer_get_cmp(); 138 | print_num(t >> 32, 16, 8); 139 | print("_"); 140 | print_num(t & 0xFFFFFFFF, 16, 8); 141 | print(" "); 142 | 143 | // loop 144 | print_num(j, 10, 2); 145 | j += 1; 146 | print("\r\n"); 147 | 148 | for (int i = 0; i < 1000; i++) 149 | { 150 | v |= *LEDS; 151 | } 152 | } 153 | 154 | return 0; 155 | } 156 | 157 | // FIN 158 | -------------------------------------------------------------------------------- /src/gpio.v: -------------------------------------------------------------------------------- 1 | 2 | module gpio 3 | #(parameter ADDR=0, AWIDTH=8) 4 | ( 5 | // cpu bus 6 | input wire wb_clk, 7 | input wire wb_rst, 8 | /* verilator lint_off UNUSED */ 9 | input [31:0] wb_dbus_adr, 10 | input [31:0] wb_dbus_dat, 11 | input [3:0] wb_dbus_sel, 12 | /* verilator lint_on UNUSED */ 13 | input wb_dbus_we, 14 | input wb_dbus_cyc, 15 | output wire [31:0] rdt, 16 | output wire ack, 17 | // IO 18 | output reg [7:0] gpio 19 | ); 20 | 21 | wire cyc; 22 | 23 | initial gpio = 0; 24 | 25 | chip_select #(.ADDR(ADDR), .WIDTH(AWIDTH)) 26 | cs_gpio ( 27 | .wb_ck(wb_clk), 28 | .addr(wb_dbus_adr[31:31-7]), 29 | .wb_cyc(wb_dbus_cyc), 30 | .wb_rst(wb_rst), 31 | .ack(ack), 32 | .cyc(cyc) 33 | ); 34 | 35 | always @(posedge wb_clk) begin 36 | if (cyc) begin 37 | if (wb_dbus_we) 38 | gpio <= wb_dbus_dat[7:0]; 39 | end 40 | end 41 | 42 | assign rdt = cyc ? { 24'h0, gpio } : 32'h0; 43 | 44 | endmodule 45 | 46 | -------------------------------------------------------------------------------- /src/ibus.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | module ibus 5 | ( 6 | input wire wb_clk, 7 | input wire wb_rst, 8 | // ibus interface 9 | input wire wb_ibus_cyc, 10 | /* verilator lint_off UNUSED */ 11 | input wire [31:0] wb_ibus_adr, 12 | /* verilator lint_on UNUSED */ 13 | output wire wb_ibus_ack, 14 | output wire [31:0] wb_ibus_rdt, 15 | // SPI interface 16 | output wire spi_cs, 17 | output wire spi_sck, 18 | output wire spi_mosi, 19 | input wire spi_miso, 20 | output wire ready 21 | ); 22 | 23 | // XiP (Execute in place) : fetch ibus requests from Flash 24 | 25 | wire spi_ready; 26 | wire start; 27 | wire ack; 28 | reg fetching = 0; 29 | 30 | assign ack = fetching & spi_ready; 31 | 32 | wire [31:0] spi_rdata; 33 | 34 | always @(posedge wb_clk) begin 35 | 36 | if (start) begin 37 | fetching <= 1; 38 | end 39 | 40 | if (fetching & spi_ready) begin 41 | fetching <= 0; 42 | end 43 | 44 | end 45 | 46 | // Endian Swap 47 | wire [31:0] rdata = { spi_rdata[7:0], spi_rdata[15:8], spi_rdata[23:16], spi_rdata[31:24] }; 48 | assign wb_ibus_rdt = wb_ibus_cyc ? rdata : 0; 49 | 50 | reg [7:0] spi_code; 51 | reg spi_tx_addr; 52 | reg spi_no_read; 53 | 54 | initial spi_code = 0; 55 | initial spi_tx_addr = 1; 56 | initial spi_no_read = 0; 57 | 58 | // SPI Flash Command Codes 59 | localparam SPI_READ = 8'h03; 60 | localparam SPI_RESET_EN = 8'h66; 61 | localparam SPI_RESET_REQ = 8'h99; 62 | 63 | // State Machine to control reset of the flash 64 | localparam RESET = 0; 65 | localparam RESET_EN_START = 1; 66 | localparam RESET_EN = 2; 67 | localparam RESET_REQ_START = 3; 68 | localparam RESET_REQ = 4; 69 | localparam WAITING = 5; 70 | localparam RUNNING = 6; 71 | 72 | reg [2:0] state; 73 | 74 | // Set to initiate SPI xfer during reset commands 75 | reg rst_start = 0; 76 | // Wait for Flash device to complete reset (~ 30us) 77 | reg [9:0] wait_ck = 0; 78 | 79 | always @(posedge wb_clk) begin 80 | 81 | if (wb_rst) begin 82 | state <= RESET; 83 | wait_ck <= 0; 84 | end 85 | 86 | if ((state == RESET) & !wb_rst) begin 87 | // Start sending the RESET_EN command 88 | state <= RESET_EN_START; 89 | spi_code <= SPI_RESET_EN; 90 | spi_tx_addr <= 0; 91 | spi_no_read <= 1; 92 | rst_start <= 1; 93 | end 94 | 95 | if (state == RESET_EN_START) begin 96 | rst_start <= 0; 97 | state <= RESET_EN; 98 | end 99 | 100 | if ((state == RESET_EN) & spi_ready) begin 101 | // Start sending the RESET_REQ command 102 | state <= RESET_REQ_START; 103 | spi_code <= SPI_RESET_REQ; 104 | spi_tx_addr <= 0; 105 | spi_no_read <= 1; 106 | rst_start <= 1; 107 | end 108 | 109 | if (state == RESET_REQ_START) begin 110 | rst_start <= 0; 111 | state <= RESET_REQ; 112 | end 113 | 114 | if ((state == RESET_REQ) & spi_ready) begin 115 | state <= WAITING; 116 | // Configure for normal running 117 | spi_code <= SPI_READ; 118 | spi_tx_addr <= 1; 119 | spi_no_read <= 0; 120 | end 121 | 122 | if (state == WAITING) begin 123 | // Wait >30us for the flash reset to complete 124 | wait_ck <= wait_ck +1; 125 | if (wait_ck == 10'h3FF) begin 126 | state <= RUNNING; 127 | end 128 | end 129 | 130 | end 131 | 132 | assign start = (state == RUNNING) ? (wb_ibus_cyc & spi_ready & !fetching) : rst_start; 133 | 134 | spi_tx spi( 135 | .ck(wb_clk), 136 | .rst(wb_rst), 137 | // SPI io 138 | .cs(spi_cs), 139 | .sck(spi_sck), 140 | .mosi(spi_mosi), 141 | .miso(spi_miso), 142 | // control 143 | .code(spi_code), 144 | .tx_addr(spi_tx_addr), 145 | .no_read(spi_no_read), 146 | // WB Bus 147 | .addr(wb_ibus_adr[23:0]), 148 | .req(start), 149 | // SPI status / data 150 | .rdata(spi_rdata), 151 | .ready(spi_ready) 152 | ); 153 | 154 | assign wb_ibus_ack = ack && (state == RUNNING); 155 | assign ready = state == RUNNING; 156 | 157 | endmodule 158 | 159 | /* 160 | * Device on dbus which makes reads to the ibus 161 | * to allow SPI flash contents to be read. 162 | * 163 | * WRITE the fetch address to BASE[0] 164 | * READ BASE[0] for the result. 165 | * 166 | * ie, to read flash location 0x100008, do 167 | * 168 | * write(base, 0x100008); // start a flash read 169 | * uint32_t data = read(base); // read the data 170 | */ 171 | 172 | module ibus_read 173 | # (parameter ADDR=0, ADDR_W=8) 174 | ( 175 | input wire wb_clk, 176 | input wire wb_rst, 177 | // dbus interface 178 | input wire wb_dbus_cyc, 179 | input wire wb_dbus_we, 180 | /* verilator lint_off UNUSED */ 181 | input wire [31:0] wb_dbus_adr, 182 | /* verilator lint_on UNUSED */ 183 | input wire [31:0] wb_dbus_dat, 184 | output wire [31:0] wb_dbus_rdt, 185 | output wire wb_dbus_ack, 186 | // ibus interface 187 | output wire wb_ibus_cyc, 188 | output wire [31:0] wb_ibus_adr, 189 | input wire wb_ibus_ack, 190 | /* verilator lint_off UNUSED */ 191 | input wire [31:0] wb_ibus_rdt, 192 | /* verilator lint_on UNUSED */ 193 | output wire dev_busy 194 | ); 195 | 196 | wire cyc; 197 | /* verilator lint_off UNUSED */ 198 | wire nowt; 199 | /* verilator lint_on UNUSED */ 200 | 201 | chip_select #(.ADDR(ADDR), .WIDTH(ADDR_W)) 202 | dev_cs ( 203 | .wb_ck(wb_clk), 204 | .addr(wb_dbus_adr[31:(32-ADDR_W)]), 205 | .wb_cyc(wb_dbus_cyc), 206 | .wb_rst(wb_rst), 207 | .ack(nowt), 208 | .cyc(cyc) 209 | ); 210 | 211 | reg ack = 0; 212 | reg ibus_ack = 0; 213 | reg [31:0] rd_addr = 0; 214 | reg [31:0] rd_data = 32'h0; 215 | reg busy = 0; 216 | 217 | assign dev_busy = busy; 218 | 219 | always @(posedge wb_clk) begin 220 | 221 | if (wb_rst) begin 222 | ack <= 0; 223 | ibus_ack <= 0; 224 | rd_addr <= 0; 225 | //busy <= 0; 226 | end 227 | 228 | if (!wb_dbus_cyc) begin 229 | ack <= 0; 230 | ibus_ack <= 0; 231 | end 232 | 233 | // dbus write 234 | if (cyc & wb_dbus_we) begin 235 | // Set the read addr 236 | rd_addr <= wb_dbus_dat; 237 | ack <= 1; 238 | end 239 | 240 | // dbus read 241 | if (cyc & !wb_dbus_we) begin 242 | if (!ibus_ack) begin 243 | // Start an ibus read cycle 244 | busy <= 1; 245 | end 246 | end 247 | 248 | // ack from ibus ends the read 249 | if (wb_ibus_ack & busy) begin 250 | busy <= 0; 251 | rd_data <= wb_ibus_rdt; 252 | ibus_ack <= 1; 253 | // Increment the read address for the next read cycle 254 | rd_addr <= rd_addr + 32'h4; 255 | end 256 | 257 | if (ibus_ack) begin 258 | ack <= 1; 259 | end 260 | 261 | end 262 | 263 | assign wb_dbus_ack = cyc & ack; 264 | assign wb_dbus_rdt = cyc ? rd_data : 0; 265 | 266 | assign wb_ibus_cyc = busy; 267 | assign wb_ibus_adr = busy ? rd_addr : 0; 268 | 269 | endmodule 270 | 271 | -------------------------------------------------------------------------------- /src/ibus_tb.v: -------------------------------------------------------------------------------- 1 | 2 | module top(); 3 | 4 | reg wb_clk = 0; 5 | 6 | always #7 wb_clk <= !wb_clk; 7 | 8 | reg wb_rst = 1; 9 | 10 | initial begin 11 | $dumpfile("serv.vcd"); 12 | $dumpvars(0, top); 13 | 14 | @(posedge wb_clk); 15 | @(posedge wb_clk); 16 | 17 | wb_rst <= 0; 18 | end 19 | 20 | // SPI Bus 21 | wire spi_cs; 22 | wire spi_sck; 23 | wire spi_mosi; 24 | reg spi_miso = 0; 25 | 26 | // iBus : CPU -> ibus_arb 27 | reg wb_ibus_cyc = 0; 28 | reg [31:0] wb_ibus_adr = 32'hZ; 29 | wire wb_ibus_ack; 30 | wire [31:0] wb_ibus_rdt; 31 | 32 | // iBus : ibus_read -> ibus_arb 33 | wire b_cyc; 34 | wire [31:0] b_adr; 35 | wire b_ack; 36 | wire [31:0] b_rdt; 37 | 38 | // Connection from ibus_arb to ibus_spi controller 39 | wire x_cyc; 40 | wire [31:0] x_adr; 41 | wire x_ack; 42 | wire [31:0] x_rdt; 43 | wire busy; 44 | 45 | bus_arb bus_arb( 46 | .wb_clk(wb_clk), 47 | .a_cyc(wb_ibus_cyc), 48 | .a_adr(wb_ibus_adr), 49 | .a_ack(wb_ibus_ack), 50 | .a_rdt(wb_ibus_rdt), 51 | .b_cyc(b_cyc), 52 | .b_adr(b_adr), 53 | .b_ack(b_ack), 54 | .b_rdt(b_rdt), 55 | .x_cyc(x_cyc), 56 | .x_adr(x_adr), 57 | .x_ack(x_ack), 58 | .x_rdt(x_rdt), 59 | .busy(busy) 60 | ); 61 | 62 | // ibus_spi controller driven by ibus_arb 63 | 64 | ibus ibus ( 65 | .wb_clk(wb_clk), 66 | .wb_rst(wb_rst), 67 | .wb_ibus_cyc(x_cyc), 68 | .wb_ibus_adr(x_adr), 69 | .wb_ibus_ack(x_ack), 70 | .wb_ibus_rdt(x_rdt), 71 | .spi_cs(spi_cs), 72 | .spi_sck(spi_sck), 73 | .spi_mosi(spi_mosi), 74 | .spi_miso(spi_miso) 75 | ); 76 | 77 | // ibus_dbus bridge device 78 | 79 | reg wb_dbus_cyc = 0; 80 | reg wb_dbus_we = 0; 81 | reg [31:0] wb_dbus_adr = 32'hZ; 82 | reg [31:0] wb_dbus_dat = 32'hZ; 83 | wire [31:0] wb_dbus_rdt; 84 | wire wb_dbus_ack; 85 | wire dev_busy; 86 | 87 | ibus_read #(.ADDR(8'h40), .ADDR_W(8)) 88 | ibus_read( 89 | .wb_clk(wb_clk), 90 | .wb_rst(wb_rst), 91 | .wb_dbus_cyc(wb_dbus_cyc), 92 | .wb_dbus_we(wb_dbus_we), 93 | .wb_dbus_adr(wb_dbus_adr), 94 | .wb_dbus_dat(wb_dbus_dat), 95 | .wb_dbus_rdt(wb_dbus_rdt), 96 | .wb_dbus_ack(wb_dbus_ack), 97 | .wb_ibus_cyc(b_cyc), 98 | .wb_ibus_adr(b_adr), 99 | .wb_ibus_ack(b_ack), 100 | .wb_ibus_rdt(b_rdt), 101 | .dev_busy(dev_busy) 102 | ); 103 | 104 | // Control / response for wb_ibus_* 105 | // Simulate CPU iBus request / response 106 | 107 | reg [31:0] ibus_data = 32'hZ; 108 | reg ibus_ready = 0; 109 | 110 | always @(posedge wb_clk) begin 111 | if (wb_ibus_ack) begin 112 | wb_ibus_cyc <= 0; 113 | ibus_data <= wb_ibus_rdt; 114 | ibus_ready <= 1; 115 | end 116 | end 117 | 118 | task ifetch(input [32:0] addr); 119 | 120 | begin 121 | wb_ibus_cyc <= 1; 122 | wb_ibus_adr <= addr; 123 | ibus_ready <= 0; 124 | end 125 | 126 | endtask 127 | 128 | reg [31:0] dbus_data = 32'hZ; 129 | reg dbus_ready = 0; 130 | 131 | always @(posedge wb_clk) begin 132 | if (wb_dbus_ack) begin 133 | wb_dbus_cyc <= 0; 134 | wb_dbus_we <= 0; 135 | wb_dbus_adr <= 32'hZ; 136 | wb_dbus_dat <= 32'hZ; 137 | if (!wb_dbus_we) begin 138 | dbus_data <= wb_dbus_rdt; 139 | dbus_ready <= 1; 140 | end 141 | end 142 | end 143 | 144 | task dwrite(input [31:0] addr, input [31:0] data); 145 | 146 | begin 147 | 148 | wb_dbus_cyc <= 1; 149 | wb_dbus_adr <= addr; 150 | wb_dbus_dat <= data; 151 | wb_dbus_we <= 1; 152 | dbus_ready <= 0; 153 | 154 | end 155 | 156 | endtask 157 | 158 | task dread(input [31:0] addr); 159 | 160 | begin 161 | 162 | wb_dbus_cyc <= 1; 163 | wb_dbus_adr <= addr; 164 | wb_dbus_we <= 0; 165 | dbus_ready <= 0; 166 | 167 | end 168 | 169 | endtask 170 | 171 | // Tests 172 | 173 | initial begin 174 | 175 | wait(!wb_rst); 176 | 177 | @(posedge wb_clk); 178 | @(posedge wb_clk); 179 | 180 | // ibus SPI controller has long reset sequence 181 | // as it has to send 0x66 0x99 commands to the Flash device 182 | // then wait 30us. 183 | 184 | spi_miso <= 0; 185 | ifetch(32'h12345678); 186 | @(posedge wb_clk); 187 | 188 | wait(ibus_ready); 189 | tb_assert(ibus_data == 0); 190 | 191 | spi_miso <= 1; 192 | ifetch(32'h12345678); 193 | @(posedge wb_clk); 194 | 195 | wait(ibus_ready); 196 | tb_assert(ibus_data == 32'hffffffff); 197 | @(posedge wb_clk); 198 | 199 | // dbus write / read cycle 200 | wait(!busy); 201 | dwrite(32'h4000_0000, 32'h12341234); 202 | @(posedge wb_clk); 203 | wait(!wb_dbus_cyc); 204 | 205 | @(posedge wb_clk); 206 | dread(32'h4000_0000); 207 | @(posedge wb_clk); 208 | wait(dbus_ready); 209 | //tb_assert(dbus_data == 32'hffffffff); 210 | @(posedge wb_clk); 211 | 212 | // Another dbus read 213 | spi_miso <= 0; 214 | @(posedge wb_clk); 215 | dread(32'h4000_0000); 216 | @(posedge wb_clk); 217 | wait(dbus_ready); 218 | tb_assert(dbus_data == 32'h00000000); 219 | @(posedge wb_clk); 220 | 221 | // Start an ifetch 222 | @(posedge wb_clk); 223 | @(posedge wb_clk); 224 | @(posedge wb_clk); 225 | @(posedge wb_clk); 226 | @(posedge wb_clk); 227 | spi_miso <= 1; 228 | ifetch(32'h12345678); 229 | 230 | // Another dbus read shortly after the ifetch 231 | @(posedge wb_clk); 232 | @(posedge wb_clk); 233 | @(posedge wb_clk); 234 | @(posedge wb_clk); 235 | dread(32'h4000_0000); 236 | 237 | // ibus read should complete first 238 | wait(ibus_ready); 239 | tb_assert(ibus_data == 32'hffffffff); 240 | 241 | // then the dbus read completes 242 | wait(dbus_ready); 243 | tb_assert(dbus_data == 32'hffffffff); 244 | @(posedge wb_clk); 245 | 246 | @(posedge wb_clk); 247 | @(posedge wb_clk); 248 | @(posedge wb_clk); 249 | @(posedge wb_clk); 250 | spi_miso <= 0; 251 | dread(32'h4000_0000); 252 | @(posedge wb_clk); 253 | wait(dbus_ready); 254 | tb_assert(dbus_data == 32'h00000000); 255 | @(posedge wb_clk); 256 | 257 | // Check Reset 258 | wb_rst <= 1; 259 | @(posedge wb_clk); 260 | @(posedge wb_clk); 261 | @(posedge wb_clk); 262 | @(posedge wb_clk); 263 | @(posedge wb_clk); 264 | @(posedge wb_clk); 265 | wb_rst <= 0; 266 | 267 | # 100000; 268 | $finish(); 269 | 270 | end 271 | 272 | endmodule 273 | 274 | 275 | -------------------------------------------------------------------------------- /src/ice40up5k_spram.v: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PicoSoC - A simple example SoC using PicoRV32 4 | * 5 | * Copyright (C) 2017 Clifford Wolf 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | */ 20 | 21 | `default_nettype none 22 | 23 | module ice40up5k_spram #( 24 | // We current always use the whole SPRAM (128 kB) 25 | parameter integer WORDS = 32768 26 | ) ( 27 | input clk, 28 | input [3:0] wen, 29 | input [21:0] addr, 30 | input [31:0] wdata, 31 | output [31:0] rdata 32 | ); 33 | 34 | wire cs_0, cs_1; 35 | wire [31:0] rdata_0, rdata_1; 36 | 37 | assign cs_0 = !addr[14]; 38 | assign cs_1 = addr[14]; 39 | assign rdata = addr[14] ? rdata_1 : rdata_0; 40 | 41 | SB_SPRAM256KA ram00 ( 42 | .ADDRESS(addr[13:0]), 43 | .DATAIN(wdata[15:0]), 44 | .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), 45 | .WREN(wen[1]|wen[0]), 46 | .CHIPSELECT(cs_0), 47 | .CLOCK(clk), 48 | .STANDBY(1'b0), 49 | .SLEEP(1'b0), 50 | .POWEROFF(1'b1), 51 | .DATAOUT(rdata_0[15:0]) 52 | ); 53 | 54 | SB_SPRAM256KA ram01 ( 55 | .ADDRESS(addr[13:0]), 56 | .DATAIN(wdata[31:16]), 57 | .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), 58 | .WREN(wen[3]|wen[2]), 59 | .CHIPSELECT(cs_0), 60 | .CLOCK(clk), 61 | .STANDBY(1'b0), 62 | .SLEEP(1'b0), 63 | .POWEROFF(1'b1), 64 | .DATAOUT(rdata_0[31:16]) 65 | ); 66 | 67 | SB_SPRAM256KA ram10 ( 68 | .ADDRESS(addr[13:0]), 69 | .DATAIN(wdata[15:0]), 70 | .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), 71 | .WREN(wen[1]|wen[0]), 72 | .CHIPSELECT(cs_1), 73 | .CLOCK(clk), 74 | .STANDBY(1'b0), 75 | .SLEEP(1'b0), 76 | .POWEROFF(1'b1), 77 | .DATAOUT(rdata_1[15:0]) 78 | ); 79 | 80 | SB_SPRAM256KA ram11 ( 81 | .ADDRESS(addr[13:0]), 82 | .DATAIN(wdata[31:16]), 83 | .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), 84 | .WREN(wen[3]|wen[2]), 85 | .CHIPSELECT(cs_1), 86 | .CLOCK(clk), 87 | .STANDBY(1'b0), 88 | .SLEEP(1'b0), 89 | .POWEROFF(1'b1), 90 | .DATAOUT(rdata_1[31:16]) 91 | ); 92 | 93 | endmodule 94 | -------------------------------------------------------------------------------- /src/icebreaker_sections.lds: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | ROM (xr) : ORIGIN = 0x00100000, LENGTH = 0x20000 4 | RAM (rw) : ORIGIN = 0x00000000, LENGTH = 0x20000 5 | } 6 | SECTIONS { 7 | .text : 8 | { 9 | . = ALIGN(4); 10 | _stext = .; 11 | *(.text) 12 | *(.text*) 13 | *(.eh_frame) 14 | . = ALIGN(4); 15 | _etext = .; 16 | _sidata = _etext; 17 | } >ROM 18 | .data : AT ( _sidata ) 19 | { 20 | . = ALIGN(4); 21 | _sdata = .; 22 | _ram_start = .; 23 | . = ALIGN(4); 24 | *(.data) 25 | *(.data*) 26 | *(.sdata) 27 | *(.sdata*) 28 | *(.rodata) 29 | *(.rodata*) 30 | *(.srodata) 31 | *(.srodata*) 32 | . = ALIGN(4); 33 | _edata = .; 34 | } >RAM 35 | .bss : 36 | { 37 | . = ALIGN(4); 38 | _sbss = .; 39 | *(.bss) 40 | *(.bss*) 41 | *(.sbss) 42 | *(.sbss*) 43 | *(COMMON) 44 | . = ALIGN(4); 45 | _ebss = .; 46 | } >RAM 47 | 48 | /* Specify the stack size */ 49 | _stack_size = 0x1000; 50 | _estack = ORIGIN(RAM) + LENGTH(RAM); 51 | _sstack = _estack - _stack_size; 52 | 53 | /* The heap is everything else */ 54 | .heap : 55 | { 56 | . = ALIGN(4); 57 | _sheap = .; 58 | _eheap = _sstack; 59 | } >RAM 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/irq.v: -------------------------------------------------------------------------------- 1 | 2 | module irq_reg 3 | #(parameter ADDR=0, ADDR_W=8, REG_WIDTH=8) 4 | ( 5 | // Wishbone bus 6 | input wire wb_clk, 7 | input wire wb_rst, 8 | /* verilator lint_off UNUSED */ 9 | input wire [31:0] wb_dbus_adr, 10 | input wire [31:0] wb_dbus_dat, 11 | /* verilator lint_on UNUSED */ 12 | input wire wb_dbus_we, 13 | input wire wb_dbus_cyc, 14 | output wire ack, 15 | output wire [31:0] rdt, 16 | // Irq lines 17 | input wire [REG_WIDTH-1:0] irq_in, 18 | output wire irq 19 | ); 20 | 21 | wire cyc; 22 | 23 | chip_select #(.ADDR(ADDR), .WIDTH(ADDR_W)) 24 | cs_irq( 25 | .wb_ck(wb_clk), 26 | .wb_rst(wb_rst), 27 | .addr(wb_dbus_adr[31:32-ADDR_W]), 28 | .wb_cyc(wb_dbus_cyc), 29 | .ack(ack), 30 | .cyc(cyc) 31 | ); 32 | 33 | reg [REG_WIDTH-1:0] enable_reg = 0; 34 | reg [REG_WIDTH-1:0] state_reg = 0; 35 | reg [REG_WIDTH-1:0] prev_reg = 0; 36 | 37 | wire [2:0] addr; 38 | assign addr = wb_dbus_adr[4:2]; 39 | 40 | wire rd, wr; 41 | assign rd = cyc & !wb_dbus_we; 42 | assign wr = cyc & wb_dbus_we; 43 | 44 | reg [REG_WIDTH-1:0] rd_data = 0; 45 | 46 | wire [REG_WIDTH-1:0] irq_detect; 47 | 48 | // detect rising edges on irq_in 49 | assign irq_detect = irq_in & ~prev_reg; 50 | 51 | always @(posedge wb_clk) begin 52 | 53 | prev_reg <= irq_in; 54 | 55 | if (rd & (addr == 0)) begin 56 | rd_data <= enable_reg; 57 | end 58 | 59 | if (rd & (addr == 1)) begin 60 | rd_data <= state_reg; 61 | end 62 | 63 | // no reg mapped for rd addr=2 64 | if (rd & (addr == 3)) begin 65 | rd_data <= irq_in; 66 | end 67 | 68 | if (wr & (addr == 0)) begin 69 | enable_reg <= wb_dbus_dat[REG_WIDTH-1:0]; 70 | end 71 | 72 | if (wr & (addr == 2)) begin 73 | // ACK the irq (clear the reg) by clearing specific bits 74 | state_reg <= state_reg & ~wb_dbus_dat[REG_WIDTH-1:0]; 75 | end 76 | 77 | if (wr & (addr == 4)) begin 78 | // set bits in enable reg 79 | enable_reg <= enable_reg | wb_dbus_dat[REG_WIDTH-1:0]; 80 | end 81 | 82 | if (wr & (addr == 5)) begin 83 | // clr bits in enable reg 84 | enable_reg <= enable_reg & ~wb_dbus_dat[REG_WIDTH-1:0]; 85 | end 86 | 87 | if (irq_detect != 0) begin 88 | // SET the irq on any enabled irq detect 89 | state_reg <= state_reg | (enable_reg & irq_detect); 90 | end 91 | 92 | end 93 | 94 | assign rdt = (rd & ack) ? { {(32-REG_WIDTH){ 1'b0 }}, rd_data } : 0; 95 | assign irq = | (state_reg & enable_reg); 96 | 97 | endmodule 98 | 99 | -------------------------------------------------------------------------------- /src/irq_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top(); 10 | 11 | reg wb_clk = 0; 12 | 13 | always #7 wb_clk <= !wb_clk; 14 | 15 | reg wb_rst = 1; 16 | 17 | initial begin 18 | $dumpfile("serv.vcd"); 19 | $dumpvars(0, top); 20 | 21 | @(posedge wb_clk); 22 | @(posedge wb_clk); 23 | 24 | wb_rst <= 0; 25 | 26 | # 5000 27 | $finish; 28 | end 29 | 30 | localparam REG_WIDTH = 8; 31 | 32 | reg [31:0] wb_dbus_dat = 32'hZ; 33 | reg [31:0] wb_dbus_adr = 32'hZ; 34 | reg wb_dbus_cyc = 0; 35 | reg wb_dbus_we = 0; 36 | wire ack; 37 | wire irq; 38 | wire [31:0] rdt; 39 | reg [REG_WIDTH-1:0] irq_in = 8'h0; 40 | 41 | reg [31:0] rd_rdt = 0; 42 | 43 | task write (input [31:0] addr, input [31:0] data); 44 | 45 | begin 46 | wb_dbus_adr <= addr; 47 | wb_dbus_dat <= data; 48 | wb_dbus_we <= 1; 49 | wb_dbus_cyc <= 1; 50 | @(posedge wb_clk); 51 | end 52 | 53 | endtask 54 | 55 | task read (input [31:0] addr); 56 | 57 | begin 58 | wb_dbus_adr <= addr; 59 | wb_dbus_we <= 0; 60 | wb_dbus_cyc <= 1; 61 | rd_rdt <= 32'hZ; 62 | @(posedge wb_clk); 63 | end 64 | 65 | endtask 66 | 67 | // simulate the CPU 68 | always @(posedge wb_clk) begin 69 | if (ack) begin 70 | if (!wb_dbus_we) begin 71 | rd_rdt <= rdt; 72 | end 73 | wb_dbus_adr <= 32'hZ; 74 | wb_dbus_dat <= 32'hZ; 75 | wb_dbus_we <= 0; 76 | wb_dbus_cyc <= 0; 77 | end 78 | end 79 | 80 | // check that rdt is never set outside of cyc cycle 81 | always @(posedge wb_clk) begin 82 | if (!wb_dbus_cyc) begin 83 | tb_assert(rdt == 0); 84 | end 85 | end 86 | 87 | irq_reg #(.ADDR(8'h30), .ADDR_W(8), .REG_WIDTH(REG_WIDTH)) 88 | irq_reg 89 | ( 90 | .wb_clk(wb_clk), 91 | .wb_rst(wb_rst), 92 | .wb_dbus_adr(wb_dbus_adr), 93 | .wb_dbus_dat(wb_dbus_dat), 94 | .wb_dbus_we(wb_dbus_we), 95 | .wb_dbus_cyc(wb_dbus_cyc), 96 | .ack(ack), 97 | .rdt(rdt), 98 | .irq_in(irq_in), 99 | .irq(irq) 100 | ); 101 | 102 | localparam base = 32'h3000_0000; 103 | localparam REG_ENABLE = base; 104 | localparam REG_STATE = base + 4; 105 | localparam REG_ACK = base + 8; 106 | localparam REG_SIGNAL = base + 12; 107 | localparam REG_SET_EN = base + 16; 108 | localparam REG_CLR_EN = base + 20; 109 | 110 | initial begin 111 | wait(!wb_rst); 112 | @(posedge wb_clk); 113 | 114 | // set irq_enable reg 115 | write(REG_ENABLE, 32'h0000_000e); 116 | wait(!wb_dbus_cyc); 117 | @(posedge wb_clk); 118 | tb_assert(!irq); 119 | 120 | // read irq_enable reg 121 | read(REG_ENABLE); 122 | @(posedge wb_clk); 123 | wait(!wb_dbus_cyc); 124 | @(posedge wb_clk); 125 | tb_assert(rd_rdt == 32'h000e); 126 | 127 | // read irq_state reg 128 | read(REG_STATE); 129 | wait(!wb_dbus_cyc); 130 | @(posedge wb_clk); 131 | @(posedge wb_clk); 132 | tb_assert(rd_rdt == 32'h0); 133 | 134 | // set an irq input line 135 | irq_in[1] <= 1; 136 | @(posedge wb_clk); 137 | 138 | // read irq_signal reg 139 | read(REG_SIGNAL); 140 | wait(!wb_dbus_cyc); 141 | @(posedge wb_clk); 142 | @(posedge wb_clk); 143 | tb_assert(rd_rdt == 32'h2); 144 | tb_assert(irq); 145 | 146 | // read irq_state reg 147 | read(REG_STATE); 148 | wait(!wb_dbus_cyc); 149 | @(posedge wb_clk); 150 | @(posedge wb_clk); 151 | tb_assert(rd_rdt == 32'h2); 152 | tb_assert(irq); 153 | 154 | // set an irq input line 155 | irq_in[3] <= 1; 156 | @(posedge wb_clk); 157 | 158 | // read irq_signal reg 159 | read(REG_SIGNAL); 160 | wait(!wb_dbus_cyc); 161 | @(posedge wb_clk); 162 | @(posedge wb_clk); 163 | tb_assert(rd_rdt == 32'ha); 164 | tb_assert(irq); 165 | 166 | // read irq_state reg 167 | read(REG_STATE); 168 | wait(!wb_dbus_cyc); 169 | @(posedge wb_clk); 170 | @(posedge wb_clk); 171 | tb_assert(rd_rdt == 32'ha); 172 | tb_assert(irq); 173 | 174 | // clr an irq line 175 | irq_in[3] <= 0; 176 | @(posedge wb_clk); 177 | 178 | // read irq_signal reg (line[3] now lo) 179 | read(REG_SIGNAL); 180 | wait(!wb_dbus_cyc); 181 | @(posedge wb_clk); 182 | @(posedge wb_clk); 183 | tb_assert(rd_rdt == 32'h2); 184 | tb_assert(irq); 185 | 186 | // read irq_state reg (line[3] still latched) 187 | read(REG_STATE); 188 | wait(!wb_dbus_cyc); 189 | @(posedge wb_clk); 190 | @(posedge wb_clk); 191 | tb_assert(rd_rdt == 32'ha); 192 | tb_assert(irq); 193 | 194 | // ACK irq on line[3] 195 | write(REG_ACK, 32'h0000_0008); 196 | wait(!wb_dbus_cyc); 197 | @(posedge wb_clk); 198 | tb_assert(irq); 199 | 200 | // read irq_state reg (line[3] now cleared) 201 | read(REG_STATE); 202 | wait(!wb_dbus_cyc); 203 | @(posedge wb_clk); 204 | @(posedge wb_clk); 205 | tb_assert(rd_rdt == 32'h2); 206 | tb_assert(irq); 207 | 208 | // ACK irq on line[1] : irq should end 209 | write(REG_ACK, 32'h0000_0002); 210 | wait(!wb_dbus_cyc); 211 | @(posedge wb_clk); 212 | tb_assert(!irq); 213 | 214 | // read irq_signal reg (line[1] still hi) 215 | read(REG_SIGNAL); 216 | wait(!wb_dbus_cyc); 217 | @(posedge wb_clk); 218 | @(posedge wb_clk); 219 | tb_assert(rd_rdt == 32'h2); 220 | tb_assert(!irq); 221 | 222 | // read irq_state reg (both irqs now cleared) 223 | read(REG_STATE); 224 | wait(!wb_dbus_cyc); 225 | @(posedge wb_clk); 226 | @(posedge wb_clk); 227 | tb_assert(rd_rdt == 32'h0); 228 | tb_assert(!irq); 229 | 230 | // irq line[0] is not irq_enabled 231 | 232 | irq_in[0] <= 1; 233 | irq_in[1] <= 0; 234 | @(posedge wb_clk); 235 | tb_assert(!irq); 236 | 237 | // read irq_signal reg (line[0] still hi) 238 | read(REG_SIGNAL); 239 | wait(!wb_dbus_cyc); 240 | @(posedge wb_clk); 241 | @(posedge wb_clk); 242 | tb_assert(rd_rdt == 32'h1); 243 | tb_assert(!irq); 244 | 245 | // read irq_state reg (no irqs active) 246 | read(REG_STATE); 247 | wait(!wb_dbus_cyc); 248 | @(posedge wb_clk); 249 | @(posedge wb_clk); 250 | tb_assert(rd_rdt == 32'h0); 251 | tb_assert(!irq); 252 | 253 | // set the enable bit in the irq reg 254 | // this should not generate an interrupt on the active line[0] 255 | // as we only detect edges. 256 | 257 | // enable irqs in line[0] 258 | write(REG_SET_EN, 32'h0000_0001); 259 | wait(!wb_dbus_cyc); 260 | @(posedge wb_clk); 261 | @(posedge wb_clk); 262 | tb_assert(!irq); 263 | 264 | // read irq_enable reg : check the bit was set 265 | read(REG_ENABLE); 266 | wait(!wb_dbus_cyc); 267 | @(posedge wb_clk); 268 | @(posedge wb_clk); 269 | tb_assert(rd_rdt == 32'h000f); 270 | tb_assert(!irq); 271 | 272 | // clr the other enable bits 273 | write(REG_CLR_EN, 32'h0000_000e); 274 | wait(!wb_dbus_cyc); 275 | @(posedge wb_clk); 276 | @(posedge wb_clk); 277 | tb_assert(!irq); 278 | 279 | // read irq_enable reg : should be just line[0] set 280 | read(REG_ENABLE); 281 | wait(!wb_dbus_cyc); 282 | @(posedge wb_clk); 283 | @(posedge wb_clk); 284 | tb_assert(rd_rdt == 32'h0001); 285 | tb_assert(!irq); 286 | 287 | // remove the line[0] irq then re-assert 288 | irq_in[0] <= 0; 289 | @(posedge wb_clk); 290 | irq_in[0] <= 1; 291 | @(posedge wb_clk); 292 | @(posedge wb_clk); 293 | // should cause irq 294 | tb_assert(irq); 295 | 296 | // disabling the irq should clear it 297 | write(REG_CLR_EN, 32'h0000_0001); 298 | wait(!wb_dbus_cyc); 299 | @(posedge wb_clk); 300 | @(posedge wb_clk); 301 | tb_assert(!irq); 302 | 303 | // However, it has not been ACKed, so will still appear active 304 | read(REG_STATE); 305 | wait(!wb_dbus_cyc); 306 | @(posedge wb_clk); 307 | @(posedge wb_clk); 308 | tb_assert(rd_rdt == 32'h1); 309 | tb_assert(!irq); 310 | 311 | // ACK irq on line[0] 312 | write(REG_ACK, 32'h0000_0001); 313 | wait(!wb_dbus_cyc); 314 | @(posedge wb_clk); 315 | tb_assert(!irq); 316 | 317 | // state should now be clear 318 | read(REG_STATE); 319 | wait(!wb_dbus_cyc); 320 | @(posedge wb_clk); 321 | @(posedge wb_clk); 322 | tb_assert(rd_rdt == 32'h0); 323 | tb_assert(!irq); 324 | 325 | // raise an irq line : not enabled yet 326 | irq_in[2] <= 1; 327 | @(posedge wb_clk); 328 | @(posedge wb_clk); 329 | tb_assert(!irq); 330 | 331 | // enable the irq : should not raise an irq 332 | write(REG_SET_EN, 32'h0000_0004); 333 | wait(!wb_dbus_cyc); 334 | @(posedge wb_clk); 335 | @(posedge wb_clk); 336 | tb_assert(!irq); 337 | 338 | end 339 | 340 | endmodule 341 | -------------------------------------------------------------------------------- /src/ram.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | module sp_ram 5 | ( 6 | input wire ck, 7 | input wire cyc, 8 | input wire we, 9 | input wire [3:0] sel, 10 | /* verilator lint_off UNUSED */ 11 | input wire [31:0] addr, 12 | /* verilator lint_on UNUSED */ 13 | input wire [31:0] wdata, 14 | output wire [31:0] rdata 15 | ); 16 | 17 | parameter WORDS = 32768; // (128 kB) 18 | 19 | wire [31:0] rd_data; 20 | wire [3:0] w_enable; 21 | 22 | assign w_enable = (cyc & we) ? sel : 4'b0; 23 | 24 | ice40up5k_spram #(.WORDS(WORDS)) 25 | ram ( 26 | .clk(ck), 27 | .wen(w_enable), 28 | .addr(addr[21:0]), 29 | .wdata(wdata), 30 | .rdata(rd_data) 31 | ); 32 | 33 | assign rdata = cyc ? rd_data : 0; 34 | 35 | endmodule 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/ram_arb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | module ram_arb 5 | # (parameter WIDTH=10) 6 | ( 7 | input wire wb_clk, 8 | 9 | // Port A 10 | input wire a_cyc, 11 | input wire a_we, 12 | input wire [3:0] a_sel, 13 | input wire [(WIDTH-1):0] a_adr, 14 | input wire [31:0] a_dat, 15 | output wire a_ack, 16 | output wire [31:0] a_rdt, 17 | 18 | // Port B 19 | input wire b_cyc, 20 | input wire b_we, 21 | input wire [3:0] b_sel, 22 | input wire [(WIDTH-1):0] b_adr, 23 | input wire [31:0] b_dat, 24 | output wire b_ack, 25 | output wire [31:0] b_rdt, 26 | 27 | // Port X 28 | output wire x_cyc, 29 | output wire x_we, 30 | output wire [3:0] x_sel, 31 | output wire [(WIDTH-1):0] x_adr, 32 | output wire [31:0] x_dat, 33 | input wire x_ack, 34 | input wire [31:0] x_rdt 35 | ); 36 | 37 | reg dev_a = 0; 38 | reg dev_b = 0; 39 | 40 | wire busy; 41 | assign busy = dev_a | dev_b; 42 | 43 | wire start; 44 | assign start = (a_cyc | b_cyc) & !busy; 45 | 46 | always @(posedge wb_clk) begin 47 | 48 | if (a_cyc & !(dev_a | dev_b)) begin 49 | dev_a <= 1; 50 | end 51 | if (b_cyc & (!dev_b) & !a_cyc) begin 52 | dev_b <= 1; 53 | end 54 | 55 | if (dev_a & !a_cyc) begin 56 | dev_a <= 0; 57 | end 58 | if (dev_b & !b_cyc) begin 59 | dev_b <= 0; 60 | end 61 | end 62 | 63 | wire a; 64 | assign a = (dev_a | start) & a_cyc; 65 | wire b; 66 | assign b = (dev_b | (start & !a_cyc)) & b_cyc; 67 | 68 | wire ar, aw; 69 | assign ar = a_ack & !a_we; 70 | assign aw = a & a_we; 71 | wire br, bw; 72 | assign br = b_ack & !b_we; 73 | assign bw = b & b_we; 74 | 75 | assign x_cyc = start | (dev_a & a_cyc) | (dev_b & b_cyc); 76 | assign x_dat = aw ? a_dat : (bw ? b_dat : 0); 77 | assign x_adr = a ? a_adr : (b ? b_adr : 0); 78 | assign x_sel = a ? a_sel : (b ? b_sel : 0); 79 | assign x_we = a ? a_we : (b ? b_we : 0); 80 | 81 | assign a_ack = dev_a & x_ack; 82 | assign b_ack = dev_b & x_ack; 83 | assign a_rdt = ar ? x_rdt : 0; 84 | assign b_rdt = br ? x_rdt : 0; 85 | 86 | endmodule 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/ram_arb_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | module tb (); 6 | 7 | initial begin 8 | $dumpfile("serv.vcd"); 9 | $dumpvars(0, tb); 10 | #5000000 $finish; 11 | end 12 | 13 | reg ck = 0; 14 | 15 | always #42 ck <= !ck; 16 | 17 | localparam WORDS = 512; 18 | localparam WIDTH = $clog2(WORDS); 19 | 20 | wire wb_clk; 21 | 22 | assign wb_clk = ck; 23 | 24 | wire x_cyc; 25 | wire x_we; 26 | wire [3:0] x_sel; 27 | wire [31:0] x_adr; 28 | wire [31:0] x_dat; 29 | wire x_ack; 30 | wire [31:0] x_rdt; 31 | 32 | // Simulate a memory device ACKing on xbus 33 | 34 | reg x_busy = 0; 35 | 36 | always @(posedge wb_clk) begin 37 | if (x_cyc & !x_ack) begin 38 | x_busy <= 1; 39 | end 40 | if (x_busy) begin 41 | x_busy <= 0; 42 | end 43 | end 44 | 45 | assign x_ack = x_busy & x_cyc; 46 | 47 | sp_ram #(.WORDS(WORDS)) sp_ram ( 48 | .ck(wb_clk), 49 | .cyc(x_cyc), 50 | .we(x_we), 51 | .sel(x_sel), 52 | .addr(x_adr), 53 | .wdata(x_dat), 54 | .rdata(x_rdt) 55 | ); 56 | 57 | assign x_ack = x_busy & x_cyc; 58 | 59 | reg a_cyc = 0; 60 | reg a_we = 0; 61 | reg [3:0] a_sel = 0; 62 | reg [31:0] a_adr = 0; 63 | reg [31:0] a_dat = 0; 64 | 65 | reg b_cyc = 0; 66 | reg b_we = 0; 67 | reg [3:0] b_sel = 0; 68 | reg [31:0] b_adr = 0; 69 | reg [31:0] b_dat = 0; 70 | 71 | wire a_ack; 72 | wire [31:0] a_rdt; 73 | wire b_ack; 74 | wire [31:0] b_rdt; 75 | 76 | ram_arb #(.WIDTH(32)) 77 | ram_arb ( 78 | .wb_clk(wb_clk), 79 | .a_cyc(a_cyc), 80 | .a_we(a_we), 81 | .a_sel(a_sel), 82 | .a_adr(a_adr), 83 | .a_dat(a_dat), 84 | .a_ack(a_ack), 85 | .a_rdt(a_rdt), 86 | .b_cyc(b_cyc), 87 | .b_we(b_we), 88 | .b_sel(b_sel), 89 | .b_adr(b_adr), 90 | .b_dat(b_dat), 91 | .b_ack(b_ack), 92 | .b_rdt(b_rdt), 93 | .x_cyc(x_cyc), 94 | .x_we(x_we), 95 | .x_sel(x_sel), 96 | .x_adr(x_adr), 97 | .x_dat(x_dat), 98 | .x_ack(x_ack), 99 | .x_rdt(x_rdt) 100 | ); 101 | 102 | reg [31:0] rd_a = 0; 103 | reg [31:0] rd_b = 0; 104 | 105 | always @(posedge wb_clk) begin 106 | if (a_ack) begin 107 | a_cyc <= 0; 108 | a_adr <= 32'hZ; 109 | a_dat <= 32'hZ; 110 | a_we <= 0; 111 | a_sel <= 0; 112 | if (!a_we) begin 113 | rd_a <= a_rdt; 114 | end 115 | end 116 | if (b_ack) begin 117 | b_cyc <= 0; 118 | b_adr <= 32'hZ; 119 | b_dat <= 32'hZ; 120 | b_we <= 0; 121 | b_sel <= 0; 122 | if (!b_we) begin 123 | rd_b <= b_rdt; 124 | end 125 | end 126 | end 127 | 128 | task write_a(input [31:0] addr, input [31:0] data, input [3:0] sel); 129 | 130 | begin 131 | a_adr <= addr; 132 | a_dat <= data; 133 | a_we <= 1; 134 | a_sel <= sel; 135 | a_cyc <= 1; 136 | end 137 | 138 | endtask 139 | 140 | task read_a(input [31:0] addr); 141 | 142 | begin 143 | a_adr <= addr; 144 | a_we <= 0; 145 | a_sel = 0; 146 | a_cyc <= 1; 147 | end 148 | 149 | endtask 150 | 151 | task write_b(input [31:0] addr, input [31:0] data, input [3:0] sel); 152 | 153 | begin 154 | b_adr <= addr; 155 | b_dat <= data; 156 | b_we <= 1; 157 | b_sel <= sel; 158 | b_cyc <= 1; 159 | end 160 | 161 | endtask 162 | 163 | task read_b(input [31:0] addr); 164 | 165 | begin 166 | b_adr <= addr; 167 | b_we <= 0; 168 | b_sel = 0; 169 | b_cyc <= 1; 170 | end 171 | 172 | endtask 173 | 174 | // check for *_rdt errors 175 | always @(posedge wb_clk) begin 176 | if (!a_cyc) begin 177 | tb_assert(a_rdt == 0); 178 | end 179 | if (!b_cyc) begin 180 | tb_assert(b_rdt == 0); 181 | end 182 | end 183 | 184 | task check_a(input [31:0] addr, input [31:0] data); 185 | 186 | begin 187 | 188 | read_a(addr); 189 | @(posedge wb_clk); 190 | wait(!a_cyc); 191 | @(posedge wb_clk); 192 | tb_assert(rd_a == data); 193 | 194 | end 195 | 196 | endtask 197 | 198 | task check_b(input [31:0] addr, input [31:0] data); 199 | 200 | begin 201 | 202 | read_b(addr); 203 | @(posedge wb_clk); 204 | wait(!b_cyc); 205 | @(posedge wb_clk); 206 | tb_assert(rd_b == data); 207 | 208 | end 209 | 210 | endtask 211 | 212 | integer addr = 0; 213 | 214 | initial begin 215 | 216 | @(posedge wb_clk); 217 | 218 | write_a(32'h0000_0020, 32'h1234_3456, 4'b1111); 219 | @(posedge wb_clk); 220 | 221 | // check that x_bus sees the signals 222 | tb_assert(x_cyc); 223 | tb_assert(x_we); 224 | tb_assert(x_sel == 4'b1111); 225 | tb_assert(x_adr == 32'h0000_0020); 226 | tb_assert(x_dat == 32'h1234_3456); 227 | 228 | // wait for a_ack : x_bus still valid 229 | wait(a_ack); 230 | tb_assert(x_cyc); 231 | tb_assert(x_we); 232 | tb_assert(x_sel == 4'b1111); 233 | tb_assert(x_adr == 32'h0000_0020); 234 | tb_assert(x_dat == 32'h1234_3456); 235 | 236 | wait(!a_cyc); 237 | @(posedge wb_clk); 238 | tb_assert(x_ack == 0); 239 | tb_assert(x_cyc == 0); 240 | tb_assert(x_we == 0); 241 | tb_assert(x_sel == 0); 242 | tb_assert(x_adr == 0); 243 | tb_assert(x_dat == 0); 244 | 245 | write_b(32'h0000_0010, 32'hcafe_cafe, 4'b1111); 246 | @(posedge wb_clk); 247 | // check that x_bus sees the signals 248 | tb_assert(x_cyc); 249 | tb_assert(x_we); 250 | tb_assert(x_sel == 4'b1111); 251 | tb_assert(x_adr == 32'h0000_0010); 252 | tb_assert(x_dat == 32'hcafe_cafe); 253 | 254 | // wait for b_ack : x_bus still valid 255 | wait(b_ack); 256 | tb_assert(x_cyc); 257 | tb_assert(x_we); 258 | tb_assert(x_sel == 4'b1111); 259 | tb_assert(x_adr == 32'h0000_0010); 260 | tb_assert(x_dat == 32'hcafe_cafe); 261 | 262 | wait(!b_cyc); 263 | @(posedge wb_clk); 264 | tb_assert(x_ack == 0); 265 | tb_assert(x_cyc == 0); 266 | tb_assert(x_we == 0); 267 | tb_assert(x_sel == 0); 268 | tb_assert(x_adr == 0); 269 | tb_assert(x_dat == 0); 270 | 271 | check_a(32'h0000_0020, 32'h1234_3456); 272 | check_a(32'h0000_0010, 32'hcafe_cafe); 273 | check_b(32'h0000_0020, 32'h1234_3456); 274 | check_b(32'h0000_0010, 32'hcafe_cafe); 275 | 276 | // Test overlapping writes 277 | 278 | // A before B 279 | write_a(32'h0000_0028, 32'h08080808, 4'b1111); 280 | @(posedge wb_clk); 281 | write_b(32'h0000_0024, 32'h04040404, 4'b1111); 282 | @(posedge wb_clk); 283 | wait(!a_cyc); 284 | wait(!b_cyc); 285 | check_a(32'h0000_0028, 32'h08080808); 286 | check_a(32'h0000_0024, 32'h04040404); 287 | 288 | // A and B together 289 | write_a(32'h0000_0000, 32'h12341234, 4'b1111); 290 | write_b(32'h0000_0004, 32'habcdabcd, 4'b1111); 291 | @(posedge wb_clk); 292 | wait(!a_cyc); 293 | wait(!b_cyc); 294 | @(posedge wb_clk); 295 | check_a(32'h0000_0000, 32'h12341234); 296 | check_a(32'h0000_0004, 32'habcdabcd); 297 | 298 | // A and B writing to the same location 299 | write_a(32'h0000_0008, 32'h12341234, 4'b1111); 300 | write_b(32'h0000_0008, 32'habcdabcd, 4'b1111); 301 | @(posedge wb_clk); 302 | wait(!a_cyc); 303 | wait(!b_cyc); 304 | @(posedge wb_clk); 305 | // A goes first, B overwrites data 306 | check_a(32'h0000_0008, 32'habcdabcd); 307 | 308 | // B before A 309 | write_b(32'h0000_0004, 32'hcafecafe, 4'b1111); 310 | @(posedge wb_clk); 311 | write_a(32'h0000_0000, 32'h34563456, 4'b1111); 312 | @(posedge wb_clk); 313 | wait(!a_cyc); 314 | wait(!b_cyc); 315 | @(posedge wb_clk); 316 | check_a(32'h0000_0004, 32'hcafecafe); 317 | check_a(32'h0000_0000, 32'h34563456); 318 | 319 | // B 2 before A 320 | write_b(32'h0000_0004, 32'h23232323, 4'b1111); 321 | @(posedge wb_clk); 322 | @(posedge wb_clk); 323 | write_a(32'h0000_0000, 32'h56565656, 4'b1111); 324 | @(posedge wb_clk); 325 | wait(!a_cyc); 326 | wait(!b_cyc); 327 | @(posedge wb_clk); 328 | check_a(32'h0000_0004, 32'h23232323); 329 | check_b(32'h0000_0000, 32'h56565656); 330 | 331 | // A 2 before B 332 | write_a(32'h0000_0004, 32'h78787878, 4'b1111); 333 | @(posedge wb_clk); 334 | @(posedge wb_clk); 335 | write_b(32'h0000_0000, 32'h12341234, 4'b1111); 336 | @(posedge wb_clk); 337 | wait(!a_cyc); 338 | wait(!b_cyc); 339 | @(posedge wb_clk); 340 | check_a(32'h0000_0004, 32'h78787878); 341 | check_b(32'h0000_0000, 32'h12341234); 342 | 343 | // Now try staged reads 344 | 345 | // Add some test data 346 | write_a(32'h0000_0000, 32'h00000000, 4'b1111); 347 | @(posedge wb_clk); 348 | wait(!a_cyc); 349 | @(posedge wb_clk); 350 | write_a(32'h0000_0004, 32'h11111111, 4'b1111); 351 | @(posedge wb_clk); 352 | wait(!a_cyc); 353 | @(posedge wb_clk); 354 | write_a(32'h0000_0008, 32'h22222222, 4'b1111); 355 | @(posedge wb_clk); 356 | wait(!a_cyc); 357 | @(posedge wb_clk); 358 | write_a(32'h0000_000c, 32'h44444444, 4'b1111); 359 | @(posedge wb_clk); 360 | wait(!a_cyc); 361 | @(posedge wb_clk); 362 | 363 | // A 2 before B 364 | read_a(32'h0000_0000); 365 | @(posedge wb_clk); 366 | @(posedge wb_clk); 367 | read_b(32'h0000_0004); 368 | wait(!a_cyc); 369 | @(posedge wb_clk); 370 | tb_assert(rd_a == 32'h00000000); 371 | wait(!b_cyc); 372 | @(posedge wb_clk); 373 | tb_assert(rd_b == 32'h11111111); 374 | 375 | // A 1 before B 376 | read_a(32'h0000_0004); 377 | @(posedge wb_clk); 378 | read_b(32'h0000_0000); 379 | wait(!a_cyc); 380 | @(posedge wb_clk); 381 | tb_assert(rd_a == 32'h11111111); 382 | wait(!b_cyc); 383 | @(posedge wb_clk); 384 | tb_assert(rd_b == 32'h00000000); 385 | 386 | // A and B together 387 | read_a(32'h0000_0000); 388 | read_b(32'h0000_0004); 389 | @(posedge wb_clk); 390 | wait(!a_cyc); 391 | @(posedge wb_clk); 392 | tb_assert(rd_a == 32'h00000000); 393 | wait(!b_cyc); 394 | @(posedge wb_clk); 395 | tb_assert(rd_b == 32'h11111111); 396 | 397 | // B 1 before A 398 | read_b(32'h0000_0008); 399 | @(posedge wb_clk); 400 | read_a(32'h0000_000c); 401 | @(posedge wb_clk); 402 | wait(!a_cyc); 403 | @(posedge wb_clk); 404 | tb_assert(rd_a == 32'h44444444); 405 | wait(!b_cyc); 406 | @(posedge wb_clk); 407 | tb_assert(rd_b == 32'h22222222); 408 | 409 | // B 2 before A 410 | read_b(32'h0000_0000); 411 | @(posedge wb_clk); 412 | @(posedge wb_clk); 413 | read_a(32'h0000_0004); 414 | @(posedge wb_clk); 415 | wait(!a_cyc); 416 | @(posedge wb_clk); 417 | tb_assert(rd_a == 32'h11111111); 418 | wait(!b_cyc); 419 | @(posedge wb_clk); 420 | tb_assert(rd_b == 32'h00000000); 421 | 422 | // Now try mixing reads and writes 423 | 424 | // read A 2 before write B 425 | read_a(32'h0000_0008); 426 | @(posedge wb_clk); 427 | @(posedge wb_clk); 428 | write_b(32'h0000_0010, 32'h44444444, 4'b1111); 429 | @(posedge wb_clk); 430 | wait(!a_cyc); 431 | wait(!b_cyc); 432 | tb_assert(rd_a == 32'h22222222); 433 | 434 | // read A 1 before write B 435 | read_a(32'h0000_0008); 436 | @(posedge wb_clk); 437 | write_b(32'h0000_0010, 32'h44444444, 4'b1111); 438 | @(posedge wb_clk); 439 | wait(!a_cyc); 440 | wait(!b_cyc); 441 | tb_assert(rd_a == 32'h22222222); 442 | @(posedge wb_clk); 443 | check_a(32'h0000_0010, 32'h44444444); 444 | 445 | // read A same time as write B 446 | read_a(32'h0000_0008); 447 | write_b(32'h0000_0014, 32'hfaceface, 4'b1111); 448 | @(posedge wb_clk); 449 | wait(!a_cyc); 450 | wait(!b_cyc); 451 | tb_assert(rd_a == 32'h22222222); 452 | @(posedge wb_clk); 453 | check_a(32'h0000_0014, 32'hfaceface); 454 | 455 | // read A same time as write B, same address 456 | // A gets priority, so read should happen first. 457 | // a second read will get the updated value. 458 | read_a(32'h0000_0014); 459 | write_b(32'h0000_0014, 32'h12345678, 4'b1111); 460 | @(posedge wb_clk); 461 | wait(!a_cyc); 462 | wait(!b_cyc); 463 | tb_assert(rd_a == 32'hfaceface); 464 | @(posedge wb_clk); 465 | check_a(32'h0000_0014, 32'h12345678); 466 | 467 | // write B 1 before read A, same address 468 | write_b(32'h0000_0010, 32'h12345678, 4'b1111); 469 | @(posedge wb_clk); 470 | read_a(32'h0000_0010); 471 | @(posedge wb_clk); 472 | wait(!a_cyc); 473 | wait(!b_cyc); 474 | tb_assert(rd_a == 32'h12345678); 475 | @(posedge wb_clk); 476 | @(posedge wb_clk); 477 | 478 | // Ensure that byte and word writes work correctly 479 | 480 | // Test byte writes 481 | addr = 32'h0000_0014; 482 | write_a(addr, 32'hxxxxxxab, 4'b0001); 483 | write_b(addr, 32'hxxxxcdxx, 4'b0010); 484 | @(posedge wb_clk); 485 | wait(!a_cyc); 486 | @(posedge wb_clk); 487 | read_a(addr); 488 | @(posedge wb_clk); 489 | wait(!b_cyc); 490 | wait(!a_cyc); 491 | @(posedge wb_clk); 492 | tb_assert((rd_a & 32'h0000ffff) == 32'h0000cdab); 493 | @(posedge wb_clk); 494 | 495 | write_a(32'h0000_0014, 32'hxxfexxxx, 4'b0100); 496 | write_b(32'h0000_0014, 32'hcaxxxxxx, 4'b1000); 497 | @(posedge wb_clk); 498 | wait(!a_cyc); 499 | wait(!b_cyc); 500 | read_a(32'h0000_0014); 501 | @(posedge wb_clk); 502 | wait(!a_cyc); 503 | wait(!b_cyc); 504 | tb_assert(rd_a == 32'hcafecdab); 505 | @(posedge wb_clk); 506 | 507 | // Test word writes 508 | write_a(addr, 32'hxxxx1234, 4'b0011); 509 | write_b(addr, 32'habcdxxxx, 4'b1100); 510 | @(posedge wb_clk); 511 | wait(!a_cyc); 512 | @(posedge wb_clk); 513 | read_a(addr); 514 | @(posedge wb_clk); 515 | wait(!b_cyc); 516 | wait(!a_cyc); 517 | @(posedge wb_clk); 518 | tb_assert(32'habcd1234); 519 | 520 | $display("done"); 521 | end 522 | 523 | endmodule 524 | 525 | -------------------------------------------------------------------------------- /src/reset.v: -------------------------------------------------------------------------------- 1 | 2 | module reset( 3 | input wire ck, 4 | input wire rst_req, 5 | output wire rst 6 | ); 7 | 8 | parameter LENGTH = 5; 9 | 10 | localparam W = $clog2(LENGTH); 11 | 12 | // Reset generator 13 | reg [W:0] rst_reg = 0; 14 | 15 | always @(posedge ck) begin 16 | if (rst_req) begin 17 | rst_reg <= 0; 18 | end else begin 19 | if (rst_reg != LENGTH) begin 20 | rst_reg <= rst_reg + 1;; 21 | end 22 | end 23 | end 24 | 25 | assign rst = rst_reg != LENGTH; 26 | 27 | endmodule 28 | 29 | -------------------------------------------------------------------------------- /src/reset_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top(); 10 | 11 | reg ck = 0; 12 | 13 | always #7 ck <= !ck; 14 | 15 | initial begin 16 | $dumpfile("serv.vcd"); 17 | $dumpvars(0, top); 18 | 19 | # 5000 ; 20 | $finish; 21 | end 22 | 23 | reg rst_req_1 = 0; 24 | wire rst_1; 25 | reset #(.LENGTH(1)) reset_1(.ck(ck), .rst_req(rst_req_1), .rst(rst_1)); 26 | 27 | reg rst_req_4 = 0; 28 | wire rst_4; 29 | reset #(.LENGTH(4)) reset_4(.ck(ck), .rst_req(rst_req_4), .rst(rst_4)); 30 | 31 | reg rst_req_17 = 0; 32 | wire rst_17; 33 | reset #(.LENGTH(17)) reset_17(.ck(ck), .rst_req(rst_req_17), .rst(rst_17)); 34 | 35 | integer i1; 36 | integer i4; 37 | integer i17; 38 | 39 | initial begin 40 | 41 | for (i1 = 0; i1 <= 1; i1 = i1 + 1) begin 42 | tb_assert(rst_1); 43 | @(posedge ck); 44 | end 45 | 46 | tb_assert(!rst_1); 47 | 48 | end 49 | 50 | initial begin 51 | 52 | for (i4 = 0; i4 <= 4; i4 = i4 + 1) begin 53 | tb_assert(rst_4); 54 | @(posedge ck); 55 | end 56 | 57 | tb_assert(!rst_4); 58 | 59 | end 60 | 61 | initial begin 62 | 63 | for (i17 = 0; i17 <= 17; i17 = i17 + 1) begin 64 | tb_assert(rst_17); 65 | @(posedge ck); 66 | end 67 | 68 | tb_assert(!rst_17); 69 | 70 | rst_req_17 <= 1; 71 | @(posedge ck); 72 | 73 | // req should hold the reset on 74 | for (i17 = 0; i17 <= 25; i17 = i17 + 1) begin 75 | @(posedge ck); 76 | tb_assert(rst_17); 77 | end 78 | 79 | // release the request, wait for reset to end .. 80 | rst_req_17 <= 0; 81 | for (i17 = 0; i17 < 17; i17 = i17 + 1) begin 82 | @(posedge ck); 83 | tb_assert(rst_17); 84 | end 85 | 86 | @(posedge ck); 87 | tb_assert(!rst_17); 88 | end 89 | 90 | endmodule 91 | 92 | -------------------------------------------------------------------------------- /src/serv.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | /* 5 | * 6 | */ 7 | 8 | module top( 9 | input wire CLK, 10 | output wire TX, 11 | output wire FLASH_SCK, 12 | output wire FLASH_SSB, 13 | output wire FLASH_IO0, 14 | input wire FLASH_IO1, 15 | output wire FLASH_IO2, 16 | output wire FLASH_IO3, 17 | output wire LED1, 18 | output wire LED2, 19 | output wire LED3, 20 | output wire LED4, 21 | output wire LED5, 22 | output wire P1A1, 23 | output wire P1A2, 24 | output wire P1A3, 25 | output wire P1A4, 26 | output wire P1B1, 27 | output wire P1B2, 28 | output wire P1B3, 29 | output wire P1B4 30 | ); 31 | 32 | // Device addresses (addr[31:24]) 33 | localparam GPIO_ADDR = 8'h40; 34 | localparam UART_ADDR = 8'h50; 35 | localparam FLASH_ADDR = 8'h70; 36 | localparam TIMER_ADDR = 8'hc0; 37 | // Run code from this location in memory (Flash) 38 | localparam RESET_PC = 32'h0010_0000; 39 | 40 | localparam RUN_SLOW = 0; // Divide the CPU clock down for development 41 | localparam RESET_LOOP = 0; // Repeatedly reset the CPU 42 | localparam TIMER_ENABLED = 1; // Hardware Timer 43 | 44 | // PLL 45 | wire pll_ck; 46 | /* verilator lint_off UNUSED */ 47 | wire locked; 48 | /* verilator lint_on UNUSED */ 49 | pll clock(.clock_in(CLK), .clock_out(pll_ck), .locked(locked)); 50 | 51 | // Conditonally slow the cpu clock down for development. 52 | generate 53 | wire ck; 54 | if (RUN_SLOW) begin 55 | reg [3:0] scale = 0; 56 | 57 | always @(posedge pll_ck) begin 58 | scale <= scale + 1; 59 | end 60 | 61 | assign ck = scale[3]; 62 | end else begin 63 | assign ck = pll_ck; 64 | end 65 | endgenerate 66 | 67 | // Reset generator 68 | wire reset_req; 69 | wire rst; 70 | 71 | reset #(.LENGTH(80)) reset(.ck(ck), .rst_req(reset_req), .rst(rst)); 72 | 73 | // Continually Reset the cpu (for development) 74 | 75 | generate 76 | if (RESET_LOOP) begin 77 | reg [(RUN_SLOW ? 21 : 24):0] reseter = 0; 78 | 79 | always @(posedge ck) begin 80 | reseter <= reseter + 1; 81 | end 82 | 83 | assign reset_req = reseter == 0; 84 | end else begin 85 | assign reset_req = 0; 86 | end 87 | endgenerate 88 | 89 | // CPU dbus 90 | 91 | wire [31:0] wb_dbus_adr; 92 | wire [31:0] wb_dbus_dat; 93 | wire [31:0] wb_dbus_rdt; 94 | wire [3:0] wb_dbus_sel; 95 | wire wb_dbus_we; 96 | wire wb_dbus_cyc; 97 | wire wb_dbus_ack; 98 | 99 | // CPU ibus 100 | 101 | wire wb_clk; 102 | wire wb_rst; 103 | wire [31:0] wb_ibus_adr; 104 | wire [31:0] wb_ibus_rdt; 105 | wire wb_ibus_cyc; 106 | wire wb_ibus_ack; 107 | 108 | assign wb_clk = ck; 109 | assign wb_rst = rst; 110 | 111 | // RAM 112 | 113 | wire ram_ack; 114 | wire ram_cyc; 115 | wire [31:0] ram_rdt; 116 | 117 | chip_select #(.ADDR(0), .WIDTH(2)) 118 | cs_ram ( 119 | .wb_ck(wb_clk), 120 | .addr(wb_dbus_adr[31:30]), 121 | .wb_cyc(wb_dbus_cyc), 122 | .wb_rst(wb_rst), 123 | .ack(ram_ack), 124 | .cyc(ram_cyc) 125 | ); 126 | 127 | // Dbus RAM 128 | 129 | sp_ram ram ( 130 | .ck(wb_clk), 131 | .addr(wb_dbus_adr), 132 | .cyc(ram_cyc), 133 | .we(wb_dbus_we), 134 | .sel(wb_dbus_sel), 135 | .wdata(wb_dbus_dat), 136 | .rdata(ram_rdt) 137 | ); 138 | 139 | // Risc-V 64-bit Timer 140 | 141 | wire timer_ack; 142 | wire timer_irq; 143 | wire [31:0] timer_rdt; 144 | 145 | generate 146 | 147 | if (TIMER_ENABLED) begin 148 | 149 | wire timer_cyc; 150 | 151 | chip_select #(.ADDR(TIMER_ADDR), .WIDTH(8)) 152 | cs_timer ( 153 | .wb_ck(wb_clk), 154 | .addr(wb_dbus_adr[31:24]), 155 | .wb_cyc(wb_dbus_cyc), 156 | .wb_rst(wb_rst), 157 | .ack(timer_ack), 158 | .cyc(timer_cyc) 159 | ); 160 | 161 | timer timer ( 162 | .wb_clk(wb_clk), 163 | .wb_rst(wb_rst), 164 | .ck_en(1'b1), // no prescale 165 | .wb_dbus_dat(wb_dbus_dat), 166 | .wb_dbus_adr(wb_dbus_adr), 167 | .wb_dbus_we(wb_dbus_we), 168 | .cyc(timer_cyc), 169 | .irq(timer_irq), 170 | .rdt(timer_rdt) 171 | ); 172 | 173 | end else begin 174 | 175 | // No timer hardware 176 | assign timer_ack = 0; 177 | assign timer_irq = 0; 178 | assign timer_rdt = 0; 179 | 180 | end 181 | endgenerate 182 | 183 | // UART 184 | 185 | wire baud_en; 186 | 187 | uart_baud #(.DIVIDE(RUN_SLOW ? 17 : 278)) uart_clock (.ck(wb_clk), .baud_ck(baud_en)); 188 | 189 | wire [31:0] uart_rdt; 190 | wire uart_ack; 191 | wire tx; 192 | /* verilator lint_off UNUSED */ 193 | wire tx_busy; 194 | /* verilator lint_on UNUSED */ 195 | 196 | uart #(.ADDR(UART_ADDR), .AWIDTH(8)) 197 | uart_io ( 198 | // cpu bus 199 | .wb_clk(wb_clk), 200 | .wb_rst(wb_rst), 201 | .wb_dbus_adr(wb_dbus_adr), 202 | .wb_dbus_dat(wb_dbus_dat), 203 | .wb_dbus_sel(wb_dbus_sel), 204 | .wb_dbus_we(wb_dbus_we), 205 | .wb_dbus_cyc(wb_dbus_cyc), 206 | .rdt(uart_rdt), 207 | .ack(uart_ack), 208 | // IO 209 | .baud_en(baud_en), 210 | .tx(tx), 211 | .busy(tx_busy) 212 | ); 213 | 214 | // GPIO 215 | 216 | wire [31:0] gpio_rdt; 217 | wire gpio_ack; 218 | 219 | /* verilator lint_off UNUSED */ 220 | wire [7:0] gpio_reg; 221 | /* verilator lint_on UNUSED */ 222 | 223 | gpio #(.ADDR(GPIO_ADDR), .AWIDTH(8)) 224 | gpio_io ( 225 | // cpu bus 226 | .wb_clk(wb_clk), 227 | .wb_rst(wb_rst), 228 | .wb_dbus_adr(wb_dbus_adr), 229 | .wb_dbus_dat(wb_dbus_dat), 230 | .wb_dbus_sel(wb_dbus_sel), 231 | .wb_dbus_we(wb_dbus_we), 232 | .wb_dbus_cyc(wb_dbus_cyc), 233 | .rdt(gpio_rdt), 234 | .ack(gpio_ack), 235 | // IO 236 | .gpio(gpio_reg) 237 | ); 238 | 239 | // SPI Flash interface 240 | 241 | wire spi_cs; 242 | wire spi_sck; 243 | wire spi_miso; 244 | wire spi_mosi; 245 | 246 | // connect to the flash chip 247 | assign FLASH_SCK = spi_sck; 248 | assign FLASH_SSB = spi_cs; 249 | assign FLASH_IO0 = spi_mosi; 250 | assign spi_miso = FLASH_IO1; 251 | assign FLASH_IO2 = 1; 252 | assign FLASH_IO3 = 1; 253 | 254 | // flash_read connection to ibus arb 255 | wire [31:0] f_adr; 256 | wire [31:0] f_rdt; 257 | wire f_cyc; 258 | wire f_ack; 259 | // flash_read dbus arb 260 | wire flash_ack; 261 | wire [31:0] flash_rdt; 262 | /* verilator lint_off UNUSED */ 263 | wire flash_busy; 264 | /* verilator lint_on UNUSED */ 265 | 266 | ibus_read #(.ADDR(FLASH_ADDR)) 267 | flash_read ( 268 | .wb_clk(wb_clk), 269 | .wb_rst(wb_rst), 270 | .wb_dbus_cyc(wb_dbus_cyc), 271 | .wb_dbus_we(wb_dbus_we), 272 | .wb_dbus_adr(wb_dbus_adr), 273 | .wb_dbus_dat(wb_dbus_dat), 274 | .wb_dbus_ack(flash_ack), 275 | .wb_dbus_rdt(flash_rdt), 276 | .wb_ibus_cyc(f_cyc), 277 | .wb_ibus_adr(f_adr), 278 | .wb_ibus_ack(f_ack), 279 | .wb_ibus_rdt(f_rdt), 280 | .dev_busy(flash_busy) 281 | ); 282 | 283 | // SPI flash ibus interface 284 | // Run XiP over this 285 | 286 | wire [31:0] s_adr; 287 | wire [31:0] s_rdt; 288 | wire s_cyc; 289 | wire s_ack; 290 | wire ibus_ready; 291 | 292 | ibus ibus ( 293 | .wb_clk(ck), 294 | .wb_rst(rst), 295 | // iBus interface 296 | .wb_ibus_adr(s_adr), 297 | .wb_ibus_rdt(s_rdt), 298 | .wb_ibus_cyc(s_cyc), 299 | .wb_ibus_ack(s_ack), 300 | // SPI interface 301 | .spi_cs(spi_cs), 302 | .spi_sck(spi_sck), 303 | .spi_miso(spi_miso), 304 | .spi_mosi(spi_mosi), 305 | .ready(ibus_ready) 306 | ); 307 | 308 | // iBus arbitration between CPU and flash_read 309 | 310 | /* verilator lint_off UNUSED */ 311 | wire arb_busy; 312 | /* verilator lint_on UNUSED */ 313 | 314 | bus_arb ibus_arb( 315 | .wb_clk(ck), 316 | // CPU is the priority channel 317 | .a_cyc(wb_ibus_cyc), 318 | .a_adr(wb_ibus_adr), 319 | .a_ack(wb_ibus_ack), 320 | .a_rdt(wb_ibus_rdt), 321 | // Flash_read at a lower priority 322 | .b_cyc(f_cyc), 323 | .b_adr(f_adr), 324 | .b_ack(f_ack), 325 | .b_rdt(f_rdt), 326 | // Connect to the ibus SPI controller 327 | .x_cyc(s_cyc), 328 | .x_adr(s_adr), 329 | .x_ack(s_ack), 330 | .x_rdt(s_rdt), 331 | .busy(arb_busy) 332 | ); 333 | 334 | // OR the dbus peripherals *_rdt & *_ack together 335 | // They must be 0 when not active. 336 | 337 | assign wb_dbus_rdt = timer_rdt | ram_rdt | uart_rdt | gpio_rdt | flash_rdt; 338 | assign wb_dbus_ack = timer_ack | ram_ack | uart_ack | gpio_ack | flash_ack; 339 | 340 | // SERV CPU 341 | 342 | parameter with_csr = 1; 343 | 344 | serv_rf_top #(.RESET_PC(RESET_PC), .WITH_CSR(with_csr)) 345 | cpu ( 346 | .clk (wb_clk), 347 | .i_rst (wb_rst), 348 | .i_timer_irq (timer_irq), 349 | // iBus 350 | .o_ibus_adr (wb_ibus_adr), 351 | .o_ibus_cyc (wb_ibus_cyc), 352 | .i_ibus_rdt (wb_ibus_rdt), 353 | .i_ibus_ack (wb_ibus_ack), 354 | // dBus 355 | .o_dbus_adr (wb_dbus_adr), 356 | .o_dbus_dat (wb_dbus_dat), 357 | .o_dbus_sel (wb_dbus_sel), 358 | .o_dbus_we (wb_dbus_we), 359 | .o_dbus_cyc (wb_dbus_cyc), 360 | .i_dbus_rdt (wb_dbus_rdt), 361 | .i_dbus_ack (wb_dbus_ack) 362 | ); 363 | 364 | // IO 365 | 366 | assign TX = tx; 367 | assign LED1 = gpio_reg[0]; 368 | assign LED2 = gpio_reg[1]; 369 | assign LED3 = gpio_reg[2]; 370 | assign LED4 = gpio_reg[3]; 371 | assign LED5 = gpio_reg[4]; 372 | 373 | // Test pins 374 | 375 | assign P1A1 = tx; 376 | assign P1A2 = timer_irq; 377 | assign P1A3 = f_cyc; 378 | assign P1A4 = tx_busy; 379 | assign P1B1 = ibus_ready; 380 | assign P1B2 = 0; 381 | assign P1B3 = 0; 382 | assign P1B4 = 0; 383 | 384 | endmodule 385 | -------------------------------------------------------------------------------- /src/serv_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top (output wire TX); 10 | 11 | wire led; 12 | assign TX = led; 13 | 14 | initial begin 15 | $dumpfile("serv.vcd"); 16 | $dumpvars(0, top); 17 | #500000 $finish; 18 | end 19 | 20 | reg CLK = 0; 21 | 22 | always #7 CLK <= !CLK; 23 | 24 | // PLL 25 | wire i_clk; 26 | assign i_clk = CLK; 27 | wire wb_clk; 28 | /* verilator lint_off UNUSED */ 29 | wire locked; 30 | /* verilator lint_on UNUSED */ 31 | pll clock(.clock_in(i_clk), .clock_out(wb_clk), .locked(locked)); 32 | 33 | // Reset generator 34 | reg [4:0] rst_reg = 5'b11111; 35 | 36 | always @(posedge wb_clk) begin 37 | rst_reg <= {1'b0, rst_reg[4:1]}; 38 | end 39 | 40 | wire wb_rst; 41 | assign wb_rst = rst_reg[0]; 42 | 43 | /* 44 | * UART Test 45 | */ 46 | 47 | wire ck; 48 | assign ck = wb_clk; 49 | 50 | reg [7:0] data_in; 51 | reg uart_we = 0; 52 | wire ready; 53 | wire tx; 54 | 55 | reg [8:0] baud = 0; 56 | 57 | always @(posedge ck) begin 58 | if (baud == 277) 59 | baud <= 0; 60 | else 61 | baud <= baud + 1; 62 | end 63 | 64 | wire baud_ck; 65 | assign baud_ck = baud == 0; 66 | 67 | uart_tx uart(.ck(ck), .baud_ck(baud_ck), .in(data_in), .we(uart_we), .ready(ready), .tx(tx)); 68 | 69 | integer i; 70 | 71 | initial begin 72 | for (i = 0; i < 10; i = i + 1) begin 73 | @(posedge ck); 74 | end 75 | data_in <= 8'haa; 76 | uart_we <= 1; 77 | @(posedge ck); 78 | uart_we <= 0; 79 | 80 | tb_assert(tx == 1); // line level 81 | @(posedge baud_ck); 82 | tb_assert(tx == 1); // line level 83 | @(posedge baud_ck); 84 | tb_assert(tx == 0); // start bit 85 | @(posedge baud_ck); 86 | tb_assert(tx == 0); // bit[0] 87 | @(posedge baud_ck); 88 | tb_assert(tx == 1); // bit[1] 89 | @(posedge baud_ck); 90 | tb_assert(tx == 0); // bit[2] 91 | @(posedge baud_ck); 92 | tb_assert(tx == 1); // bit[3] 93 | @(posedge baud_ck); 94 | tb_assert(tx == 0); // bit[4] 95 | @(posedge baud_ck); 96 | tb_assert(tx == 1); // bit[5] 97 | @(posedge baud_ck); 98 | tb_assert(tx == 0); // bit[6] 99 | tb_assert(ready == 0); 100 | @(posedge baud_ck); 101 | tb_assert(tx == 1); // bit[7] 102 | tb_assert(ready == 0); 103 | @(posedge ck); 104 | @(posedge ck); 105 | tb_assert(ready == 1); 106 | 107 | // ready is set, so we can load the next byte 108 | @(posedge ck); 109 | data_in <= 8'h55; 110 | uart_we <= 1; 111 | @(posedge ck); 112 | uart_we <= 0; 113 | 114 | @(posedge baud_ck); 115 | tb_assert(tx == 1); // stop bit 116 | tb_assert(ready == 0); 117 | @(posedge baud_ck); 118 | tb_assert(tx == 0); // start bit 119 | @(posedge baud_ck); 120 | tb_assert(tx == 1); // bit[0] 121 | @(posedge baud_ck); 122 | tb_assert(tx == 0); // bit[1] 123 | @(posedge baud_ck); 124 | tb_assert(tx == 1); // bit[2] 125 | @(posedge baud_ck); 126 | tb_assert(tx == 0); // bit[3] 127 | @(posedge baud_ck); 128 | tb_assert(tx == 1); // bit[4] 129 | @(posedge baud_ck); 130 | tb_assert(tx == 0); // bit[5] 131 | @(posedge baud_ck); 132 | tb_assert(tx == 1); // bit[6] 133 | @(posedge baud_ck); 134 | tb_assert(tx == 0); // bit[7] 135 | @(posedge baud_ck); 136 | tb_assert(tx == 1); // stop bit 137 | end 138 | 139 | endmodule 140 | -------------------------------------------------------------------------------- /src/sim.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | /* 5 | * 6 | */ 7 | 8 | module sp_ram 9 | ( 10 | input wire ck, 11 | input wire cyc, 12 | input wire we, 13 | input wire [3:0] sel, 14 | /* verilator lint_off UNUSED */ 15 | input wire [31:0] addr, 16 | /* verilator lint_on UNUSED */ 17 | input wire [31:0] wdata, 18 | output reg [31:0] rdata 19 | ); 20 | 21 | wire [3:0] w_enable; 22 | 23 | assign w_enable = (cyc & we) ? sel : 4'b0; 24 | 25 | parameter WORDS = 256; 26 | 27 | reg [31:0] mem [0:WORDS-1]; 28 | 29 | always @(posedge ck) begin 30 | rdata <= mem[addr]; 31 | if (w_enable[0]) mem[addr][ 7: 0] <= wdata[ 7: 0]; 32 | if (w_enable[1]) mem[addr][15: 8] <= wdata[15: 8]; 33 | if (w_enable[2]) mem[addr][23:16] <= wdata[23:16]; 34 | if (w_enable[3]) mem[addr][31:24] <= wdata[31:24]; 35 | end 36 | 37 | endmodule 38 | 39 | -------------------------------------------------------------------------------- /src/soc.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "soc.h" 6 | 7 | //#define TIMER ((uint32_t volatile *) 0xc0000000) 8 | #define uart ((uint32_t volatile *) 0x50000000) 9 | 10 | /* 11 | * _sbrk() is used by malloc() to alloc heap memory. 12 | */ 13 | 14 | // Memory locations defined in the linker config. 15 | extern "C" uint32_t _sheap, _eheap; 16 | 17 | extern "C" void *_sbrk(intptr_t increment) 18 | { 19 | static void *heap = (void*) & _sheap; 20 | 21 | void *base = heap; 22 | 23 | void *next = & ((char *) base)[increment]; 24 | 25 | if (next >= (void*) & _eheap) 26 | { 27 | errno = ENOMEM; 28 | return (void*) -1; 29 | } 30 | 31 | heap = next; 32 | return base; 33 | } 34 | 35 | /* 36 | * Timer 37 | */ 38 | 39 | #define TIMER_MTIME_LO 0 40 | #define TIMER_MTIME_HI 1 41 | #define TIMER_MTIMECMP_LO 2 42 | #define TIMER_MTIMECMP_HI 3 43 | 44 | void timer_set(uint64_t t) 45 | { 46 | TIMER[TIMER_MTIMECMP_LO] = t & 0xffffffff; 47 | asm volatile ("" : : : "memory"); 48 | TIMER[TIMER_MTIMECMP_HI] = t >> 32; 49 | } 50 | 51 | uint64_t timer_get() 52 | { 53 | const uint32_t lo = TIMER[TIMER_MTIME_LO]; 54 | asm volatile ("" : : : "memory"); 55 | const uint32_t hi = TIMER[TIMER_MTIME_HI]; 56 | 57 | return lo + (((uint64_t) hi) << 32); 58 | } 59 | 60 | uint64_t timer_get_cmp() 61 | { 62 | const uint32_t lo = TIMER[TIMER_MTIMECMP_LO]; 63 | asm volatile ("" : : : "memory"); 64 | const uint32_t hi = TIMER[TIMER_MTIMECMP_HI]; 65 | 66 | return lo + (((uint64_t) hi) << 32); 67 | } 68 | 69 | void timer_wait(uint64_t period) 70 | { 71 | const uint64_t now = timer_get(); 72 | const uint64_t end = now + period; 73 | 74 | while (timer_get() < end) 75 | ; 76 | } 77 | 78 | /* 79 | * 80 | */ 81 | 82 | void soc_putc(uint8_t c) 83 | { 84 | *uart = c; 85 | } 86 | 87 | void print(const char *text) 88 | { 89 | for (const char *s = text; *s; s++) 90 | { 91 | soc_putc(*s); 92 | } 93 | } 94 | 95 | /* 96 | * 97 | */ 98 | 99 | void print_num(uint32_t n, uint32_t base, uint32_t digits) 100 | { 101 | if (digits > 1) 102 | { 103 | print_num(n / base, base, digits-1); 104 | } 105 | 106 | n %= base; 107 | const uint8_t c = (n > 9) ? ('a' + n - 10) : ('0' + n); 108 | soc_putc(c); 109 | } 110 | 111 | void print_dec(uint32_t n) 112 | { 113 | print_num(n, 10, 8); 114 | } 115 | 116 | void print_hex(uint32_t n, uint32_t digits) 117 | { 118 | print_num(n, 16, digits); 119 | } 120 | 121 | // FIN 122 | -------------------------------------------------------------------------------- /src/soc.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | */ 5 | 6 | #define LEDS ((uint32_t volatile*) 0x40000000) 7 | #define flash ((uint32_t volatile*) 0x70000000) 8 | #define IRQ ((uint32_t volatile*) 0x80000000) 9 | #define TIMER ((uint32_t volatile*) 0xc0000000) 10 | 11 | /* 12 | * IRQ Controller 13 | */ 14 | 15 | #define IRQ_ENABLE 0 16 | #define IRQ_STATE 1 17 | #define IRQ_ACK 2 18 | #define IRQ_SIGNAL 3 19 | #define IRQ_SET_EN 4 20 | #define IRQ_CLR_EN 5 21 | 22 | inline void irq_set_enable(uint32_t d) 23 | { 24 | IRQ[IRQ_SET_EN] = d; 25 | } 26 | 27 | inline void irq_ack(uint32_t d) 28 | { 29 | IRQ[IRQ_ACK] = d; 30 | } 31 | 32 | inline uint32_t irq_state() 33 | { 34 | return IRQ[IRQ_STATE]; 35 | } 36 | 37 | /* 38 | * CSR access functions 39 | */ 40 | 41 | inline uint32_t read_mie() 42 | { 43 | uint32_t value; 44 | __asm__ volatile ("csrr %0, mie" : "=r"(value)); 45 | return value; 46 | } 47 | 48 | inline void write_mie(uint32_t value) 49 | { 50 | __asm__ volatile ("csrw mie, %0" : : "r"(value)); 51 | } 52 | 53 | inline uint32_t read_mtvec() 54 | { 55 | uint32_t value; 56 | __asm__ volatile ("csrr %0, mtvec" : "=r"(value)); 57 | return value; 58 | } 59 | 60 | inline void write_mtvec(uint32_t value) 61 | { 62 | __asm__ volatile ("csrw mtvec, %0" : : "r"(value)); 63 | } 64 | 65 | inline uint32_t read_mstatus() 66 | { 67 | uint32_t value; 68 | __asm__ volatile ("csrr %0, mstatus" : "=r"(value)); 69 | return value; 70 | } 71 | 72 | inline void write_mstatus(uint32_t value) 73 | { 74 | __asm__ volatile ("csrw mstatus, %0" : : "r"(value)); 75 | } 76 | 77 | inline uint32_t read_mcause() 78 | { 79 | uint32_t value; 80 | __asm__ volatile ("csrr %0, mcause" : "=r"(value)); 81 | return value; 82 | } 83 | 84 | inline void write_mcause(uint32_t value) 85 | { 86 | __asm__ volatile ("csrw mcause, %0" : : "r"(value)); 87 | } 88 | 89 | /* 90 | * Timer 91 | */ 92 | 93 | void timer_set(uint64_t t); 94 | uint64_t timer_get(); 95 | uint64_t timer_get_cmp(); 96 | void timer_wait(uint64_t period); 97 | 98 | /* 99 | * 100 | */ 101 | 102 | void print(const char *text); 103 | void print_num(uint32_t n, uint32_t base, uint32_t digits); 104 | void print_dec(uint32_t n); 105 | void print_hex(uint32_t n, uint32_t digits); 106 | 107 | // FIN 108 | -------------------------------------------------------------------------------- /src/spi.v: -------------------------------------------------------------------------------- 1 | 2 | module spi_tx( 3 | input wire ck, 4 | input wire rst, 5 | output wire cs, 6 | output wire sck, 7 | output wire mosi, 8 | input wire miso, 9 | 10 | input wire [7:0] code, 11 | input wire [23:0] addr, 12 | input wire tx_addr, 13 | input wire no_read, 14 | input wire req, 15 | output reg [31:0] rdata, 16 | output wire ready 17 | ); 18 | 19 | initial begin 20 | rdata = 0; 21 | end 22 | 23 | reg [31:0] tx = 32'b0; 24 | reg [6:0] bit_count = 0; 25 | wire sending; 26 | assign sending = bit_count != 0; 27 | 28 | reg clock = 0; 29 | 30 | function [7:0] reverse(input [7:0] d); 31 | 32 | begin 33 | reverse = { d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] }; 34 | end 35 | 36 | endfunction 37 | 38 | /* verilator lint_off UNUSED */ 39 | reg [31:0] rx = 32'b0; 40 | /* verilator lint_on UNUSED */ 41 | 42 | always @(posedge ck) begin 43 | 44 | if (rst) begin 45 | bit_count <= 0; 46 | end 47 | 48 | clock <= !clock; 49 | 50 | if (req) begin 51 | if (tx_addr) 52 | tx <= { reverse(addr[7:0]), reverse(addr[15:8]), reverse(addr[23:16]), reverse(code) }; 53 | else 54 | tx <= { 24'h0, reverse(code) }; 55 | 56 | bit_count <= 8 + (tx_addr ? 24 : 0) + (no_read ? 0 : 32); 57 | 58 | clock <= 0; 59 | end 60 | 61 | if (clock) begin 62 | if (sending) begin 63 | 64 | if (bit_count == 1) begin 65 | rdata <= { rx[30:0], miso }; 66 | end 67 | 68 | bit_count <= bit_count - 1; 69 | tx <= { 1'b0, tx[31:1] }; 70 | rx <= { rx[30:0], miso }; 71 | 72 | end 73 | end 74 | end 75 | 76 | assign mosi = sending ? tx[0] : 1; 77 | assign sck = sending ? clock : 0; 78 | assign cs = !sending; 79 | assign ready = !sending; 80 | 81 | endmodule 82 | 83 | -------------------------------------------------------------------------------- /src/spi_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top(); 10 | 11 | reg ck = 0; 12 | 13 | always #50 ck <= !ck; 14 | 15 | initial begin 16 | $dumpfile("serv.vcd"); 17 | $dumpvars(0, top); 18 | end 19 | 20 | reg rst_req = 0; 21 | wire rst; 22 | reset #(.LENGTH(5)) reset(.ck(ck), .rst_req(rst_req), .rst(rst)); 23 | 24 | wire cs; 25 | wire sck; 26 | wire mosi; 27 | reg miso = 0; 28 | wire [31:0] rdata; 29 | wire ready; 30 | 31 | reg [7:0] code = 8'h03; 32 | reg [23:0] addr = 24'hZ; 33 | reg tx_addr = 1; 34 | reg no_read = 0; 35 | reg req = 0; 36 | 37 | spi_tx spi_tx( 38 | .ck(ck), 39 | .rst(rst), 40 | .cs(cs), 41 | .sck(sck), 42 | .mosi(mosi), 43 | .miso(miso), 44 | .code(code), 45 | .addr(addr), 46 | .tx_addr(tx_addr), 47 | .no_read(no_read), 48 | .req(req), 49 | .rdata(rdata), 50 | .ready(ready) 51 | ); 52 | 53 | task read(input [23:0] rd_addr); 54 | 55 | begin 56 | addr <= rd_addr; 57 | req <= 1; 58 | @(posedge ck); 59 | req <= 0; 60 | @(posedge ck); 61 | end 62 | 63 | endtask 64 | 65 | realtime t1, t2; 66 | integer i, t3; 67 | 68 | initial begin 69 | wait(!rst); 70 | @(posedge ck); 71 | 72 | // normal read "03 aa aa aa dd dd dd dd" cycle 73 | miso <= 0; 74 | tx_addr <= 1; 75 | no_read <= 0; 76 | t1 = $realtime; 77 | read(24'h123456); 78 | 79 | wait(ready); 80 | t2 = $realtime; 81 | @(posedge ck); 82 | tb_assert(rdata == 32'h00000000); 83 | tb_assert(cs); 84 | tb_assert(!sck); 85 | tb_assert(mosi); 86 | 87 | // check the cycle takes (64-bit * 2) + 1 88 | t3 = t2 - t1; 89 | tb_assert(t3 == ((1 + (64 * 2)) * 100)); 90 | 91 | // short write "03" 92 | miso <= 1; 93 | tx_addr <= 0; 94 | no_read <= 1; 95 | t1 = $realtime; 96 | read(24'h123456); 97 | 98 | wait(ready); 99 | t2 = $realtime; 100 | @(posedge ck); 101 | //tb_assert(rdata == 32'hffffffff); 102 | tb_assert(cs); 103 | tb_assert(!sck); 104 | tb_assert(mosi); 105 | 106 | t3 = t2 - t1; 107 | tb_assert(t3 == ((1 + (8 * 2)) * 100)); 108 | 109 | // short write, normal read "03 dd dd dd dd" 110 | miso <= 0; 111 | tx_addr <= 0; 112 | no_read <= 0; 113 | t1 = $realtime; 114 | read(24'h123456); 115 | 116 | wait(ready); 117 | t2 = $realtime; 118 | @(posedge ck); 119 | tb_assert(rdata == 32'h00000000); 120 | tb_assert(cs); 121 | tb_assert(!sck); 122 | tb_assert(mosi); 123 | 124 | t3 = t2 - t1; 125 | tb_assert(t3 == ((1 + (40 * 2)) * 100)); 126 | 127 | // normal read "03 aa aa aa dd dd dd dd" cycle 128 | // interrupted by reset 129 | miso <= 1; 130 | tx_addr <= 1; 131 | no_read <= 0; 132 | t1 = $realtime; 133 | read(24'haaaaaa); 134 | 135 | for (i = 0; i < 50; i = i + 1) begin 136 | @(posedge ck); 137 | end 138 | 139 | // reset request 140 | rst_req <= 1; 141 | @(posedge ck); 142 | rst_req <= 0; 143 | 144 | wait(ready); 145 | t2 = $realtime; 146 | @(posedge ck); 147 | //tb_assert(rdata == 32'hffffffff); 148 | tb_assert(cs); 149 | tb_assert(!sck); 150 | tb_assert(mosi); 151 | 152 | // check the cycle takes less than (64-bit * 2) + 1 153 | t3 = t2 - t1; 154 | tb_assert(t3 < ((1 + (64 * 2)) * 100)); 155 | 156 | // wait for end of reset 157 | wait(!rst); 158 | @(posedge ck); 159 | 160 | // short write "03" 161 | miso <= 1; 162 | tx_addr <= 0; 163 | no_read <= 1; 164 | t1 = $realtime; 165 | read(24'h123456); 166 | 167 | wait(ready); 168 | t2 = $realtime; 169 | @(posedge ck); 170 | tb_assert(cs); 171 | tb_assert(!sck); 172 | tb_assert(mosi); 173 | 174 | t3 = t2 - t1; 175 | tb_assert(t3 == ((1 + (8 * 2)) * 100)); 176 | 177 | @(posedge ck); 178 | @(posedge ck); 179 | $finish; 180 | end 181 | 182 | endmodule 183 | 184 | -------------------------------------------------------------------------------- /src/start.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | start: 4 | 5 | # zero-initialize register file 6 | addi x1, zero, 0 7 | # 8 | addi x3, zero, 0 9 | addi x4, zero, 0 10 | addi x5, zero, 0 11 | addi x6, zero, 0 12 | addi x7, zero, 0 13 | addi x8, zero, 0 14 | addi x9, zero, 0 15 | addi x10, zero, 0 16 | addi x11, zero, 0 17 | addi x12, zero, 0 18 | addi x13, zero, 0 19 | addi x14, zero, 0 20 | addi x15, zero, 0 21 | addi x16, zero, 0 22 | addi x17, zero, 0 23 | addi x18, zero, 0 24 | addi x19, zero, 0 25 | addi x20, zero, 0 26 | addi x21, zero, 0 27 | addi x22, zero, 0 28 | addi x23, zero, 0 29 | addi x24, zero, 0 30 | addi x25, zero, 0 31 | addi x26, zero, 0 32 | addi x27, zero, 0 33 | addi x28, zero, 0 34 | addi x29, zero, 0 35 | addi x30, zero, 0 36 | addi x31, zero, 0 37 | 38 | # Point sp to the end of RAM 39 | la sp, _estack 40 | 41 | # Copy initialised data from flash to RAM 42 | # 43 | # ibus_read device is mapped to 0x70000000 44 | 45 | # Write the address to flash bridge dev 46 | li a0, 0x70000000 47 | la a1, _sidata # start of .data section in ROM 48 | sw a1, 0(a0) 49 | 50 | la a2, _sdata # start of .data section in RAM 51 | la a3, _edata # end of .data section in RAM 52 | bge a2, a3, end_init_data 53 | loop_init_data: 54 | # read data from ROM. Addr is incremented in h/w. 55 | lw a1, 0(a0) 56 | # save in RAM 57 | sw a1, 0(a2) 58 | addi a2, a2, 4 59 | blt a2, a3, loop_init_data 60 | 61 | end_init_data: 62 | 63 | 64 | # zero-init bss section 65 | la a0, _sbss 66 | la a1, _ebss 67 | bge a0, a1, end_init_bss 68 | loop_init_bss: 69 | sw zero, 0(a0) 70 | addi a0, a0, 4 71 | blt a0, a1, loop_init_bss 72 | end_init_bss: 73 | 74 | # call main 75 | 76 | call main 77 | loop: 78 | j loop 79 | 80 | # FIN 81 | -------------------------------------------------------------------------------- /src/tb.v: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | */ 5 | 6 | task tb_assert(input test); 7 | 8 | begin 9 | if (!test) begin 10 | $display("ASSERTION FAILED in %m"); 11 | $finish; 12 | end 13 | end 14 | 15 | endtask 16 | 17 | /* 18 | * 19 | */ 20 | 21 | module SB_PLL40_PAD 22 | #( 23 | parameter FEEDBACK_PATH = 0, 24 | parameter [3:0] DIVR = 0, 25 | parameter [2:0] DIVQ = 0, 26 | parameter [2:0] FILTER_RANGE = 0, 27 | parameter [6:0] DIVF = 0 28 | ) 29 | ( 30 | /* verilator lint_off UNUSED */ 31 | input RESETB, 32 | input PACKAGEPIN, 33 | input BYPASS, 34 | output PLLOUTCORE = 0, 35 | output LOCK = 1 36 | /* verilator lint_on UNUSED */ 37 | ); 38 | 39 | always @(posedge PACKAGEPIN) begin 40 | PLLOUTCORE <= !PLLOUTCORE; 41 | end 42 | 43 | endmodule 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/timer.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | module timer 5 | #(parameter INITIAL = 64'h0) 6 | ( 7 | input wire wb_clk, 8 | input wire wb_rst, 9 | input wire ck_en, 10 | input wire [31:0] wb_dbus_dat, 11 | /* verilator lint_off UNUSED */ 12 | input wire [31:0] wb_dbus_adr, 13 | /* verilator lint_on UNUSED */ 14 | input wire wb_dbus_we, 15 | input wire cyc, 16 | output reg irq, 17 | output wire [31:0] rdt 18 | ); 19 | 20 | reg [63:0] mtime; 21 | reg [63:0] mtimecmp; 22 | // temp registers needed to make 64-bit read/writes atomic 23 | reg [31:0] temp_time; 24 | reg [31:0] temp_cmp; 25 | 26 | initial begin 27 | irq = 0; 28 | mtime = INITIAL; 29 | mtimecmp = 0; 30 | end 31 | 32 | wire [1:0] addr; 33 | assign addr = wb_dbus_adr[3:2]; 34 | 35 | always @(posedge wb_clk) begin 36 | 37 | if (wb_rst) begin 38 | mtime <= INITIAL; 39 | mtimecmp <= 0; 40 | end else if (ck_en) begin 41 | mtime <= mtime + 1; 42 | end 43 | 44 | irq <= (mtimecmp != 0) ? (mtime > mtimecmp) : 0; 45 | 46 | if (cyc & wb_dbus_we) begin 47 | if (addr == 2) begin 48 | // latch mtimecmp_lo 49 | temp_cmp <= wb_dbus_dat; 50 | end 51 | if (addr == 3) begin 52 | // mtimecmp_hi + latched lo word 53 | mtimecmp[31:0] <= temp_cmp; 54 | mtimecmp[63:32] <= wb_dbus_dat; 55 | end 56 | end 57 | 58 | if (cyc & !wb_dbus_we) begin 59 | if (addr == 0) begin 60 | // mtime_lo : latch the hi word 61 | temp_time <= mtime[63:32]; 62 | end 63 | end 64 | 65 | end 66 | 67 | function [31:0] read(input [1:0] reg_addr); 68 | 69 | begin 70 | case (reg_addr) 71 | 0 : read = mtime[31:0]; 72 | 1 : read = temp_time; 73 | 2 : read = mtimecmp[31:0]; 74 | 3 : read = mtimecmp[63:32]; 75 | endcase 76 | end 77 | 78 | endfunction 79 | 80 | assign rdt = (cyc & !wb_dbus_we) ? read(addr) : 0; 81 | 82 | endmodule 83 | 84 | -------------------------------------------------------------------------------- /src/timer_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top(); 10 | 11 | reg wb_clk = 0; 12 | 13 | always #7 wb_clk <= !wb_clk; 14 | 15 | reg wb_rst = 1; 16 | 17 | initial begin 18 | $dumpfile("serv.vcd"); 19 | $dumpvars(0, top); 20 | 21 | @(posedge wb_clk); 22 | @(posedge wb_clk); 23 | 24 | wb_rst <= 0; 25 | 26 | # 5000 27 | $finish; 28 | end 29 | 30 | reg [31:0] wb_dbus_dat = 32'hZ; 31 | reg [31:0] wb_dbus_adr = 32'hZ; 32 | reg wb_dbus_cyc = 0; 33 | reg wb_dbus_we = 0; 34 | wire ack; 35 | wire cyc; 36 | wire irq; 37 | wire [31:0] rdt; 38 | 39 | chip_select #(.ADDR(8'hc0), .WIDTH(8)) 40 | cs_timer ( 41 | .wb_ck(wb_clk), 42 | .addr(wb_dbus_adr[31:24]), 43 | .wb_cyc(wb_dbus_cyc), 44 | .wb_rst(wb_rst), 45 | .ack(ack), 46 | .cyc(cyc) 47 | ); 48 | 49 | // force initial mtime setting to test for 32-bit rollover 50 | localparam start = 64'h12345678fffffff0; 51 | 52 | timer #(.INITIAL(start)) timer ( 53 | .wb_clk(wb_clk), 54 | .wb_rst(wb_rst), 55 | .ck_en(1'b1), 56 | .wb_dbus_dat(wb_dbus_dat), 57 | .wb_dbus_adr(wb_dbus_adr), 58 | .wb_dbus_we(wb_dbus_we), 59 | .cyc(cyc), 60 | .irq(irq), 61 | .rdt(rdt) 62 | ); 63 | 64 | task write (input [31:0] addr, input [31:0] data); 65 | 66 | begin 67 | wb_dbus_adr <= addr; 68 | wb_dbus_dat <= data; 69 | wb_dbus_we <= 1; 70 | wb_dbus_cyc <= 1; 71 | @(posedge wb_clk); 72 | end 73 | 74 | endtask 75 | 76 | reg [31:0] rd_rdt = 0; 77 | 78 | task read (input [31:0] addr); 79 | 80 | begin 81 | wb_dbus_adr <= addr; 82 | wb_dbus_we <= 0; 83 | wb_dbus_cyc <= 1; 84 | rd_rdt <= 32'hZ; 85 | @(posedge wb_clk); 86 | end 87 | 88 | endtask 89 | 90 | // simulate the CPU 91 | always @(posedge wb_clk) begin 92 | if (ack) begin 93 | if (!wb_dbus_we) begin 94 | rd_rdt <= rdt; 95 | end 96 | wb_dbus_adr <= 32'hZ; 97 | wb_dbus_dat <= 32'hZ; 98 | wb_dbus_we <= 0; 99 | wb_dbus_cyc <= 0; 100 | end 101 | end 102 | 103 | // check that rdt is never set outside of cyc cycle 104 | always @(posedge wb_clk) begin 105 | if (!cyc) begin 106 | tb_assert(rdt == 0); 107 | end 108 | end 109 | 110 | // clock should track mtime 111 | reg [63:0] clock = start; 112 | reg [63:0] now = 0; 113 | 114 | always @(posedge wb_clk) begin 115 | if (wb_rst) begin 116 | clock <= start; 117 | now <= 0; 118 | end else begin 119 | clock <= clock + 1; 120 | end 121 | 122 | // latch the time an irq occurred 123 | if (irq & (now == 0)) begin 124 | now <= clock; 125 | end 126 | end 127 | 128 | integer i; 129 | 130 | initial begin 131 | wait(!wb_rst); 132 | @(posedge wb_clk); 133 | 134 | // write mtimecmp 135 | write(32'hc0000008, 32'h12341234); 136 | wait(!cyc); 137 | @(posedge wb_clk); 138 | write(32'hc000000c, 32'habcdabcd); 139 | wait(!cyc); 140 | @(posedge wb_clk); 141 | 142 | // read mtimecmp 143 | read(32'hc0000008); 144 | wait(!cyc); 145 | @(posedge wb_clk); 146 | tb_assert(rd_rdt == 32'h12341234); 147 | 148 | read(32'hc000000c); 149 | wait(!cyc); 150 | @(posedge wb_clk); 151 | tb_assert(rd_rdt == 32'habcdabcd); 152 | 153 | // check roll-over of mtime reads 154 | now <= clock + 1; 155 | @(posedge wb_clk); 156 | // assert we are just about to roll over 157 | tb_assert(now == 64'h12345678fffffffd); 158 | 159 | read(32'hc0000000); 160 | wait(!cyc); 161 | @(posedge wb_clk); 162 | tb_assert(rd_rdt == now[31:0]); 163 | 164 | // pause before reading the hi word 165 | for (i = 0; i < 20; i = i + 1) begin 166 | @(posedge wb_clk); 167 | end 168 | 169 | // the hi word should have been frozen at the lo read 170 | read(32'hc0000004); 171 | wait(!cyc); 172 | @(posedge wb_clk); 173 | tb_assert(rd_rdt == now[63:32]); 174 | 175 | // check we have rolled over 176 | now <= clock + 1; 177 | read(32'hc0000000); 178 | wait(!cyc); 179 | @(posedge wb_clk); 180 | tb_assert(rd_rdt == now[31:0]); 181 | 182 | read(32'hc0000004); 183 | wait(!cyc); 184 | @(posedge wb_clk); 185 | tb_assert(rd_rdt == 32'h12345679); 186 | 187 | // Check reset 188 | wb_rst <= 1; 189 | @(posedge wb_clk); 190 | @(posedge wb_clk); 191 | @(posedge wb_clk); 192 | @(posedge wb_clk); 193 | wb_rst <= 0; 194 | 195 | // check mtime was reset 196 | read(32'hc0000000); 197 | wait(!cyc); 198 | @(posedge wb_clk); 199 | tb_assert(rd_rdt == 32'hfffffff0); 200 | 201 | read(32'hc0000004); 202 | wait(!cyc); 203 | @(posedge wb_clk); 204 | tb_assert(rd_rdt == 32'h12345678); 205 | 206 | // check mtimecmp was reset 207 | read(32'hc0000008); 208 | wait(!cyc); 209 | @(posedge wb_clk); 210 | @(posedge wb_clk); 211 | tb_assert(rd_rdt == 0); 212 | 213 | read(32'hc000000c); 214 | wait(!cyc); 215 | @(posedge wb_clk); 216 | tb_assert(rd_rdt == 0); 217 | 218 | // Test interrupt on timer compare 219 | 220 | // reset 221 | wb_rst <= 1; 222 | @(posedge wb_clk); 223 | @(posedge wb_clk); 224 | @(posedge wb_clk); 225 | @(posedge wb_clk); 226 | wb_rst <= 0; 227 | 228 | // set mtimecmp to interrupt on rollover 229 | write(32'hc0000008, 32'h0); 230 | wait(!cyc); 231 | @(posedge wb_clk); 232 | 233 | write(32'hc000000c, 32'h12345679); 234 | wait(!cyc); 235 | @(posedge wb_clk); 236 | @(posedge wb_clk); 237 | 238 | tb_assert(!irq); 239 | wait(irq); 240 | 241 | // read mtime 242 | read(32'hc0000000); 243 | wait(!cyc); 244 | @(posedge wb_clk); 245 | @(posedge wb_clk); 246 | tb_assert(rd_rdt[31:8] == 24'h0); 247 | 248 | read(32'hc0000004); 249 | wait(!cyc); 250 | @(posedge wb_clk); 251 | @(posedge wb_clk); 252 | tb_assert(rd_rdt == 32'h12345679); 253 | 254 | tb_assert(irq); 255 | 256 | // Clear the irq by writing a higher mtimecmp value 257 | write(32'hc0000008, 32'hffff0000); 258 | wait(!cyc); 259 | @(posedge wb_clk); 260 | 261 | write(32'hc000000c, 32'h12345679); 262 | wait(!cyc); 263 | @(posedge wb_clk); 264 | @(posedge wb_clk); 265 | tb_assert(!irq); 266 | 267 | @(posedge wb_clk); 268 | @(posedge wb_clk); 269 | 270 | // Check for read/write interleaved 271 | 272 | wb_rst <= 1; 273 | @(posedge wb_clk); 274 | @(posedge wb_clk); 275 | wb_rst <= 0; 276 | 277 | @(posedge wb_clk); 278 | 279 | read (32'hc0000000); // read mtime lo 280 | wait(!cyc); 281 | @(posedge wb_clk); 282 | @(posedge wb_clk); 283 | tb_assert(rd_rdt == 32'hfffffff1); 284 | 285 | write(32'hc0000008, 32'hbbbbbbbb); // write cmp 286 | wait(!cyc); 287 | @(posedge wb_clk); 288 | write(32'hc000000c, 32'haaaaaaaa); // write cmp 289 | wait(!cyc); 290 | @(posedge wb_clk); 291 | 292 | read (32'hc0000004); // read mtime hi 293 | wait(!cyc); 294 | @(posedge wb_clk); 295 | @(posedge wb_clk); 296 | tb_assert(rd_rdt == 32'h12345678); 297 | 298 | @(posedge wb_clk); 299 | @(posedge wb_clk); 300 | $finish; 301 | end 302 | 303 | endmodule 304 | 305 | -------------------------------------------------------------------------------- /src/uart.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | 4 | module uart_tx( 5 | input wire ck, 6 | input wire baud_ck, 7 | input wire [7:0] in, 8 | input wire we, 9 | output reg ready, 10 | output reg tx 11 | ); 12 | 13 | reg [9:0] shift = 10'h3ff; 14 | reg [3:0] count = 0; 15 | 16 | initial ready = 1; 17 | initial tx = 1; 18 | 19 | always @(posedge ck) begin 20 | 21 | if (baud_ck) begin 22 | shift <= { 1'b1, shift[9:1] }; 23 | if (count != 0) begin 24 | count <= count - 1; 25 | end 26 | 27 | ready <= count == 0; 28 | tx <= shift[0]; 29 | end 30 | 31 | if (we & ready) begin 32 | shift <= { 1'b1, in, 1'b0 }; 33 | count <= 9; 34 | ready <= 0; 35 | end 36 | 37 | end 38 | 39 | endmodule 40 | 41 | /* 42 | * 43 | */ 44 | 45 | module uart_baud 46 | #(parameter DIVIDE=16) 47 | (input wire ck, 48 | output reg baud_ck 49 | ); 50 | 51 | localparam WIDTH = $clog2(DIVIDE); 52 | reg [(WIDTH-1):0] baud = 0; 53 | 54 | initial baud_ck = 0; 55 | 56 | always @(posedge ck) begin 57 | /* verilator lint_off WIDTH */ 58 | if (baud == (DIVIDE - 1)) begin 59 | baud <= 0; 60 | baud_ck <= 1; 61 | end else begin 62 | baud <= baud + 1; 63 | baud_ck <= 0; 64 | end 65 | /* verilator lint_on WIDTH */ 66 | end 67 | 68 | endmodule 69 | 70 | /* 71 | * 72 | */ 73 | 74 | module uart 75 | #(parameter ADDR=0, AWIDTH=8) 76 | ( 77 | // cpu bus 78 | input wire wb_clk, 79 | input wire wb_rst, 80 | /* verilator lint_off UNUSED */ 81 | input wire [31:0] wb_dbus_adr, 82 | input wire [31:0] wb_dbus_dat, 83 | input wire [3:0] wb_dbus_sel, 84 | /* verilator lint_on UNUSED */ 85 | input wire wb_dbus_we, 86 | input wire wb_dbus_cyc, 87 | output wire [31:0] rdt, 88 | output wire ack, 89 | // IO 90 | input wire baud_en, 91 | output wire tx, 92 | output wire busy 93 | ); 94 | 95 | wire cyc; 96 | wire uart_ready; 97 | wire cs_ack; 98 | reg ready = 0; 99 | 100 | chip_select #(.ADDR(ADDR), .WIDTH(AWIDTH)) 101 | cs_uart ( 102 | .wb_ck(wb_clk), 103 | .addr(wb_dbus_adr[31:31-7]), 104 | .wb_cyc(wb_dbus_cyc), 105 | .wb_rst(wb_rst), 106 | .ack(cs_ack), 107 | .cyc(cyc) 108 | ); 109 | 110 | uart_tx uart( 111 | .ck(wb_clk), 112 | .baud_ck(baud_en), 113 | .in(wb_dbus_dat[7:0]), 114 | .we(cyc & wb_dbus_we), 115 | .ready(uart_ready), 116 | .tx(tx)); 117 | 118 | always @(posedge wb_clk) begin 119 | 120 | if (cyc) begin 121 | // allow immeadiate ack if not busy 122 | ready <= uart_ready; 123 | end 124 | 125 | end 126 | 127 | assign rdt = 0; 128 | assign ack = (cyc & ready) ? cs_ack : (cyc & uart_ready); 129 | assign busy = !uart_ready; 130 | 131 | endmodule 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/uart_tb.v: -------------------------------------------------------------------------------- 1 | 2 | `default_nettype none 3 | `timescale 1ns / 100ps 4 | 5 | /* 6 | * 7 | */ 8 | 9 | module top (); 10 | 11 | reg wb_clk = 0; 12 | 13 | always #7 wb_clk <= !wb_clk; 14 | 15 | reg wb_rst = 1; 16 | 17 | initial begin 18 | $dumpfile("serv.vcd"); 19 | $dumpvars(0, top); 20 | 21 | @(posedge wb_clk); 22 | @(posedge wb_clk); 23 | 24 | wb_rst <= 0; 25 | 26 | # 50000; 27 | $finish; 28 | end 29 | 30 | // cpu bus 31 | reg [31:0] wb_dbus_adr = 32'hZ; 32 | reg [31:0] wb_dbus_dat = 32'hZ; 33 | reg [3:0] wb_dbus_sel = 4'h0; 34 | reg wb_dbus_we = 1'b0; 35 | reg wb_dbus_cyc = 1'b0; 36 | wire [31:0] rdt; 37 | wire ack; 38 | wire baud_en; 39 | wire tx; 40 | wire busy; 41 | 42 | assign baud_en = 1; 43 | 44 | uart #(.ADDR(8'h40), .AWIDTH(8)) 45 | uart ( 46 | .wb_clk(wb_clk), 47 | .wb_rst(wb_rst), 48 | .wb_dbus_adr(wb_dbus_adr), 49 | .wb_dbus_dat(wb_dbus_dat), 50 | .wb_dbus_sel(wb_dbus_sel), 51 | .wb_dbus_we(wb_dbus_we), 52 | .wb_dbus_cyc(wb_dbus_cyc), 53 | .rdt(rdt), 54 | .ack(ack), 55 | .baud_en(baud_en), 56 | .tx(tx), 57 | .busy(busy) 58 | ); 59 | 60 | always @(posedge wb_clk) begin 61 | if (ack) begin 62 | wb_dbus_adr <= 32'hZ; 63 | wb_dbus_dat <= 32'hZ; 64 | wb_dbus_sel <= 4'h0; 65 | wb_dbus_we <= 0; 66 | wb_dbus_cyc <= 0; 67 | end 68 | end 69 | 70 | task write(input [31:0] addr, input [31:0] data); 71 | 72 | begin 73 | wb_dbus_adr <= addr; 74 | wb_dbus_dat <= data; 75 | wb_dbus_sel <= 4'h1; 76 | wb_dbus_we <= 1; 77 | wb_dbus_cyc <= 1; 78 | end 79 | 80 | endtask 81 | 82 | always @(posedge wb_clk) begin 83 | if (!wb_dbus_cyc) begin 84 | tb_assert(rdt == 0); 85 | end 86 | end 87 | 88 | integer i; 89 | 90 | initial begin 91 | wait(!wb_rst); 92 | @(posedge wb_clk); 93 | 94 | tb_assert(!busy); 95 | 96 | write(32'h4000_0000, 32'h40); 97 | @(posedge wb_clk); 98 | @(posedge wb_clk); 99 | tb_assert(wb_dbus_cyc == 0); 100 | tb_assert(busy); 101 | @(posedge wb_clk); 102 | @(posedge wb_clk); 103 | 104 | write(32'h4000_0000, 32'haa); 105 | @(posedge wb_clk); 106 | tb_assert(busy); 107 | @(posedge wb_clk); 108 | 109 | wait(!wb_dbus_cyc); 110 | tb_assert(busy); 111 | 112 | // 10 cycles to shift out the tx data 113 | // 1 cycle to indicate not busy 114 | for (i = 0; i < 11; i = i + 1) begin 115 | tb_assert(busy); 116 | @(posedge wb_clk); 117 | end 118 | 119 | tb_assert(!busy); 120 | @(posedge wb_clk); 121 | @(posedge wb_clk); 122 | @(posedge wb_clk); 123 | @(posedge wb_clk); 124 | 125 | write(32'h4000_0000, 32'hfe); 126 | @(posedge wb_clk); 127 | wait(!wb_dbus_cyc); 128 | tb_assert(busy); 129 | write(32'h4000_0000, 32'h01); 130 | @(posedge wb_clk); 131 | wait(!wb_dbus_cyc); 132 | tb_assert(busy); 133 | end 134 | 135 | endmodule 136 | --------------------------------------------------------------------------------