├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── cmake ├── riscv.cmake ├── synthesis_ice40.cmake └── wishbone_intercon.cmake ├── firmware ├── bootstrap.cpp ├── main.cpp ├── sections.ld └── start.s ├── hx4k_pmod.pcf ├── img └── soc_blink.gif ├── rtl ├── fifo.v ├── gen │ ├── soc_wb.core │ ├── wb_intercon.v │ └── wb_intercon.vh ├── ice_pll.v ├── rgb_led_wb.v ├── timer_wb.v ├── top.v ├── uart_rx.v ├── uart_tx.v ├── uart_wb.v └── wb_ram.v ├── scripts └── makehex.py ├── sim.gtkw ├── sim └── main.cpp ├── test ├── fifo_test.cpp ├── test_main.cpp ├── test_utils.hpp └── wb_ram_test.cpp ├── vendor ├── catch │ ├── ParseAndAddCatchTests.cmake │ └── catch.hpp └── vexriscv │ ├── GenVexRiscv.scala │ └── VexRiscv.v └── wb_intercon.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/wb_intercon"] 2 | path = vendor/wb_intercon 3 | url = https://github.com/olofk/wb_intercon.git 4 | [submodule "vendor/verilog-arbiter"] 5 | path = vendor/verilog-arbiter 6 | url = https://github.com/bmartini/verilog-arbiter.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.3) 2 | 3 | project(ice40_soc C CXX) 4 | 5 | # Load our extra FPGA function definitions 6 | include(cmake/synthesis_ice40.cmake) 7 | include(cmake/wishbone_intercon.cmake) 8 | include(cmake/riscv.cmake) 9 | 10 | # Set some paths 11 | get_filename_component(VENDOR_DIR ${PROJECT_SOURCE_DIR}/vendor/ ABSOLUTE) 12 | get_filename_component(PLACEMENTS_DIR ${PROJECT_SOURCE_DIR}/placements/ ABSOLUTE) 13 | get_filename_component(SCRIPTS_DIR ${PROJECT_SOURCE_DIR}/scripts/ ABSOLUTE) 14 | 15 | # Tests need to know where to find catch2 16 | include_directories(${VENDOR_DIR}/catch/) 17 | 18 | # Ensure verilator CMake support is present 19 | find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT}) 20 | if (NOT verilator_FOUND) 21 | message(FATAL_ERROR "Could not find Verilator. Install or set $VERILATOR_ROOT") 22 | endif() 23 | 24 | # Integrate the catch tests with cmake's make test support 25 | include(${VENDOR_DIR}/catch/ParseAndAddCatchTests.cmake) 26 | enable_testing() 27 | 28 | # Nextpnr args 29 | set(NEXTPNR_ARGS --${FPGA_DEVICE} --package ${FPGA_PACKAGE}) 30 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --pcf ${PROJECT_SOURCE_DIR}/${PLACEMENT_FILE}) 31 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --freq ${FREQ_MHZ}) 32 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --json ${PROJECT_NAME}.json) 33 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --asc ${PROJECT_NAME}.asc) 34 | 35 | 36 | set(PROJECT_TOP top) 37 | set(PLACEMENT_FILE hx4k_pmod.pcf) 38 | set(FPGA_DEVICE hx8k) 39 | set(FPGA_PACKAGE tq144:4k) 40 | set(FREQ_MHZ 42) 41 | set(SYNTHESIS_ARGS "-abc9 -dsp") 42 | set(VERILOG_SOURCES 43 | rtl/top.v 44 | rtl/ice_pll.v 45 | rtl/fifo.v 46 | rtl/timer_wb.v 47 | rtl/rgb_led_wb.v 48 | rtl/uart_rx.v 49 | rtl/uart_tx.v 50 | rtl/uart_wb.v 51 | rtl/wb_ram.v 52 | # Generated sources 53 | rtl/gen/wb_intercon.v 54 | # Vendored sources 55 | ${VENDOR_DIR}/wb_intercon/rtl/verilog/wb_arbiter.v 56 | ${VENDOR_DIR}/wb_intercon/rtl/verilog/wb_mux.v 57 | ${VENDOR_DIR}/verilog-arbiter/src/arbiter.v 58 | ${VENDOR_DIR}/vexriscv/VexRiscv.v 59 | ) 60 | # Create a synthesis target for the gateware 61 | synth_ice40(${PROJECT_NAME}) 62 | 63 | # Create a wishbone generation target, and have it be a dependency of the 64 | # synthesis target 65 | wishbone_gen( 66 | PROJNAME ${PROJECT_NAME} 67 | CONFIG_FILE "${PROJECT_SOURCE_DIR}/wb_intercon.yaml" 68 | MODULE_NAME "wb_intercon" 69 | FOR_TARGET ${PROJECT_NAME}_synth 70 | ) 71 | 72 | # Set up the firmware build 73 | set(FIRMWARE_SOURCES 74 | firmware/start.s 75 | firmware/bootstrap.cpp 76 | firmware/main.cpp 77 | ) 78 | set(FIRMWARE_LDSCRIPT 79 | firmware/sections.ld 80 | ) 81 | 82 | # Create a firmware target, and have it also be a dependency of the synth target 83 | compile_riscv( 84 | NAME ${PROJECT_NAME}_fw 85 | ARCH "rv32i" 86 | SOURCES ${FIRMWARE_SOURCES} 87 | LDSCRIPT ${FIRMWARE_LDSCRIPT} 88 | FOR_TARGET ${PROJECT_NAME}_synth 89 | ) 90 | 91 | # Simulation target 92 | add_executable(${PROJECT_NAME}_sim 93 | sim/main.cpp 94 | ) 95 | verilate(${PROJECT_NAME}_sim COVERAGE TRACE 96 | INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/rtl" 97 | VERILATOR_ARGS -O2 -x-assign 0 --top-module top 98 | SOURCES ${VERILOG_SOURCES} 99 | ) 100 | 101 | # Sim depends on the firmware / wb gen 102 | add_dependencies(${PROJECT_NAME}_sim 103 | ${PROJECT_NAME}_fw_build 104 | ${PROJECT_NAME}_wishbone_gen_wb_intercon 105 | ) 106 | 107 | # Individual module tests 108 | macro(define_test_target TEST_NAME) 109 | add_executable(${PROJECT_NAME}_${TEST_NAME}_test 110 | test/test_main.cpp 111 | test/${TEST_NAME}_test.cpp) 112 | verilate(${PROJECT_NAME}_${TEST_NAME}_test COVERAGE TRACE 113 | INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/test" 114 | VERILATOR_ARGS -O2 -x-assign 0 115 | SOURCES ${PROJECT_SOURCE_DIR}/rtl/${TEST_NAME}.v 116 | ) 117 | ParseAndAddCatchTests(${PROJECT_NAME}_${TEST_NAME}_test) 118 | endmacro() 119 | define_test_target(wb_ram) 120 | define_test_target(fifo) 121 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Ross Schlaikjer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FPGA Soft CPU SoC Demonstration 2 | 3 | This repo contains the files associated with 4 | [this blog post](https://rhye.org/post/fpgas-3-softcores/), 5 | which covers: 6 | 7 | - Customizing and generating a VexRiscv CPU core 8 | - Creating custom wishbone peripherals and attaching them to the CPU 9 | - Linking and bringup of a bare-metal RISC-V firmware image 10 | - Memory mapped IO 11 | - RISC-V interrupt handling 12 | 13 | ![SoC with Interrupts + Peripheral Demo](/img/soc_blink.gif) 14 | 15 | ## Repo layout 16 | 17 | This repo is broken out in the following main subfolders: 18 | 19 | - cmake: Custom function definitions for generating wishbone interconnects, 20 | FPGA synthesis targets and RISC-V firmware targets 21 | - firmware: Linker scripts, startup files and application code for the firmware 22 | that runs on the demo SoC 23 | - rtl: All non-vendored Verilog code used to genreate the SoC. This includes 24 | all wishbone peripherals (Interrupt timer, RGB LED controller and buffered UART), 25 | the device-specific PLL and the top-level module. 26 | - scripts: Small scripts used as part of the build process 27 | - sim: Source code for simulating the entire design under verilator and 28 | generating a trace file. 29 | - test: Test cases against individual Verilog modules. These tests are built 30 | using the [catch2](https://github.com/catchorg/Catch2) 31 | framework, and can be invoked using the `make test` target. 32 | - vendor: Third party code necessary for building the SoC. Primarily, 33 | - Catch2, the testing framework 34 | - wb_intercon, a custom wishbone interconnect generator 35 | - verilog-arbiter, a dependency of wb_intercon 36 | - The generated VexRiscv core and associated SpinalHDL configuration 37 | 38 | ## Building 39 | 40 | In order to fully build this repo, one must have all of 41 | - cmake 42 | - yosys / nextpnr 43 | - verilator 44 | - riscv-gcc 45 | 46 | If deploying to the hardware target shown in the blog post and 47 | [this repo](https://github.com/rschlaikjer/hx4k-pmod), 48 | you will also need the 49 | [faff](https://github.com/rschlaikjer/faff) 50 | programming utility. 51 | 52 | Steps are provided below to install each component on a Debian based system. 53 | 54 | ### Yosys 55 | 56 | ``` 57 | sudo apt install -y checkinstall build-essential clang bison flex \ 58 | libreadline-dev gawk tcl-dev libffi-dev git \ 59 | graphviz xdot pkg-config python3 libboost-system-dev \ 60 | libboost-python-dev libboost-filesystem-dev zlib1g-dev 61 | 62 | git clone https://github.com/YosysHQ/yosys.git 63 | cd yosys 64 | make config-gcc 65 | make -j$(nproc) 66 | checkinstall -D -y --pkgname yosys 67 | yosys -V 68 | ``` 69 | 70 | ### Icestorm 71 | 72 | ``` 73 | sudo apt install -y build-essential clang bison flex libreadline-dev \ 74 | gawk tcl-dev libffi-dev git mercurial graphviz \ 75 | xdot pkg-config python python3 libftdi-dev \ 76 | qt5-default python3-dev libboost-all-dev cmake 77 | git clone https://github.com/cliffordwolf/icestorm.git 78 | cd icestorm 79 | make -j$(nproc) 80 | checkinstall -D -y --pkgname icestorm 81 | ``` 82 | 83 | ### Nextpnr 84 | 85 | Note that this configuration only includes the icestorm target, not the ECP5 86 | targets. Install project trellis and use `-DARCH=all` to enable ECP5 support. 87 | 88 | ``` 89 | sudo apt install -y clang-format qt5-default python3-dev libboost-dev \ 90 | libboost-filesystem-dev libboost-thread-dev \ 91 | libboost-program-options-dev libboost-python-dev \ 92 | libboost-iostreams-dev libboost-dev libeigen3-dev 93 | 94 | git clone https://github.com/YosysHQ/nextpnr.git 95 | cd nextpnr 96 | cmake -DARCH=ice40 . 97 | make -j$(nproc) 98 | checkinstall -D -y --pkgname nextpnr 99 | ``` 100 | 101 | ### Verilator 102 | 103 | ``` 104 | sudo apt install -y git make autoconf g++ flex bison 105 | git clone https://github.com/verilator/verilator.git -b v4.028 106 | cd verilator 107 | unset VERILATOR_ROOT 108 | autoconf 109 | ./configure 110 | make -j$(nproc) 111 | checkinstall -D -y --pkgname verilator --pkgversion 4.028 112 | ``` 113 | 114 | ### RISC-V GCC 115 | 116 | ``` 117 | sudo apt install -y autoconf automake autotools-dev curl python3 \ 118 | libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex \ 119 | texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev 120 | git clone --recursive https://github.com/riscv/riscv-gnu-toolchain.git 121 | cd riscv-gnu-toolchain 122 | ./configure --prefix=/opt/riscv32i/ --with-arch=rv32i 123 | make -j$(nproc) && sudo make install 124 | export PATH="/opt/riscv32i/bin:${PATH}" # Add to your .bashrc or equivalent 125 | ``` 126 | 127 | ## Running simulations / tests 128 | 129 | In order to simulate the entire design for a given number of cycles, build 130 | and run the target `ice40_soc_sim`: 131 | 132 | cd fpga-3-softcores 133 | mkdir build 134 | cd build 135 | cmake ../ 136 | make ice40_soc_sim && ./ice40_soc_sim 137 | 138 | This will produce a file called `sim_out.vcd` in your build directory, which 139 | can be opened with Gtkwave. 140 | 141 | In order to build and run tests: 142 | 143 | # Build the test binaries 144 | make ice40_soc_wb_ram_test ice40_soc_fifo_test 145 | # Run tests 146 | make test 147 | 148 | ## Deploying to hardware 149 | 150 | As written, this repo is designed to run on 151 | [this dev board](https://github.com/rschlaikjer/hx4k-pmod), 152 | but should be quite portable to other devices. 153 | If running on the HX4K board, then running 154 | 155 | make && faff -f ice40_soc.bit 156 | 157 | will build the bitstream and load it onto the board. 158 | 159 | If porting to another platform, the placement file and chip specificiations may 160 | be changed near the top of the master CMakeLists.txt 161 | -------------------------------------------------------------------------------- /cmake/riscv.cmake: -------------------------------------------------------------------------------- 1 | cmake_policy(SET CMP0057 NEW) 2 | 3 | function(compile_riscv) 4 | 5 | cmake_parse_arguments( 6 | CONF 7 | "" 8 | "NAME;ARCH;LDSCRIPT;FOR_TARGET" 9 | "SOURCES;CFLAGS" 10 | ${ARGN} 11 | ) 12 | 13 | # Locate the riscv toolchain components 14 | set(RISCV_PREFIX riscv32-unknown-elf) 15 | find_program(RISCV_CXX ${RISCV_PREFIX}-g++) 16 | find_program(RISCV_OBJCOPY ${RISCV_PREFIX}-objcopy) 17 | 18 | # Find the bin -> reversed hex tool 19 | find_program(MAKEHEX_PY makehex.py ${SCRIPTS_DIR}) 20 | 21 | # Resolve paths relative to the source dirs 22 | get_filename_component(CONF_LDSCRIPT ${CONF_LDSCRIPT} ABSOLUTE) 23 | 24 | set(SOURCES_STR "") 25 | foreach(source ${CONF_SOURCES}) 26 | if(NOT IS_ABSOLUTE ${source}) 27 | get_filename_component(source ${source} ABSOLUTE) 28 | endif() 29 | if (NOT ${source} IN_LIST QUALIFIED_SOURCES) 30 | list(APPEND QUALIFIED_SOURTCES ${source}) 31 | set(SOURCES_STR ${SOURCES_STR} ${source}) 32 | endif() 33 | endforeach() 34 | 35 | # Compile command for the elf itself 36 | set(ELF_FILE ${CONF_NAME}_elf) 37 | set(CFLAGS -g -march=${CONF_ARCH} --static -nostartfiles -ffreestanding ${CONF_CFLAGS}) 38 | set(LDFLAGS -Wl,-Bstatic,-T,${CONF_LDSCRIPT},-Map,${CONF_NAME}.map) 39 | add_custom_command( 40 | OUTPUT ${ELF_FILE} 41 | COMMAND ${RISCV_CXX} ARGS ${CFLAGS} ${LDFLAGS} -o ${ELF_FILE} ${SOURCES_STR} 42 | DEPENDS ${SOURCES_STR} 43 | ) 44 | 45 | # Generate a raw binary file from the elf 46 | set(BIN_FILE ${CONF_NAME}_bin) 47 | add_custom_command( 48 | OUTPUT ${BIN_FILE} 49 | DEPENDS ${ELF_FILE} 50 | COMMAND ${RISCV_OBJCOPY} ARGS -O binary ${ELF_FILE} ${BIN_FILE} 51 | ) 52 | 53 | # Generate an endian-swapped hex file, for loading with $readmemh 54 | set(HEX_FILE ${CONF_NAME}_hex) 55 | add_custom_command( 56 | OUTPUT ${HEX_FILE} 57 | DEPENDS ${BIN_FILE} 58 | COMMAND ${MAKEHEX_PY} ARGS ${BIN_FILE} > ${HEX_FILE} 59 | ) 60 | 61 | add_custom_target(${CONF_NAME}_build 62 | DEPENDS ${HEX_FILE} 63 | ) 64 | 65 | add_dependencies(${CONF_FOR_TARGET} ${CONF_NAME}_build) 66 | 67 | endfunction() 68 | -------------------------------------------------------------------------------- /cmake/synthesis_ice40.cmake: -------------------------------------------------------------------------------- 1 | cmake_policy(SET CMP0057 NEW) 2 | function(synth_ice40 TARGET_NAME) 3 | 4 | find_program(YOSYS_CMD yosys) 5 | find_program(NEXTPNR_ICE40_CMD nextpnr-ice40) 6 | find_program(ICEPACK_CMD icepack) 7 | find_program(FPGA_PROG_CMD faff) 8 | 9 | # Compose the yosys command 10 | set(YOSYS_READ_VERILOG "") 11 | foreach(source ${VERILOG_SOURCES}) 12 | if(NOT IS_ABSOLUTE ${source}) 13 | get_filename_component(source ${source} ABSOLUTE) 14 | endif() 15 | if (NOT ${source} IN_LIST QUALIFIED_VERILOG_SOURCES) 16 | list(APPEND QUALIFIED_VERILOG_SOURCES ${source}) 17 | set(YOSYS_READ_VERILOG ${YOSYS_READ_VERILOG} " read_verilog ${source}") 18 | endif() 19 | endforeach() 20 | set(YOSYS_CMDLINE ${YOSYS_READ_VERILOG} " synth_ice40 -top ${PROJECT_TOP} -json ${PROJECT_NAME}.json ${SYNTHESIS_ARGS}") 21 | 22 | # Nextpnr args 23 | set(NEXTPNR_ARGS --${FPGA_DEVICE} --package ${FPGA_PACKAGE}) 24 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --pcf ${PROJECT_SOURCE_DIR}/${PLACEMENT_FILE}) 25 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --freq ${FREQ_MHZ}) 26 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --json ${PROJECT_NAME}.json) 27 | set(NEXTPNR_ARGS ${NEXTPNR_ARGS} --asc ${PROJECT_NAME}.asc) 28 | 29 | # Packing 30 | set(ICEPACK_ARGS ${PROJECT_NAME}.asc ${PROJECT_NAME}.bit) 31 | 32 | # Synthesize 33 | add_custom_command( 34 | OUTPUT ${PROJECT_NAME}.json 35 | COMMAND ${YOSYS_CMD} ARGS -p "${YOSYS_CMDLINE}" 36 | DEPENDS ${QUALIFIED_VERILOG_SOURCES} 37 | VERBATIM 38 | ) 39 | 40 | # Place & route 41 | add_custom_command( 42 | OUTPUT ${PROJECT_NAME}.asc 43 | COMMAND ${NEXTPNR_ICE40_CMD} ARGS ${NEXTPNR_ARGS} 44 | DEPENDS ${PROJECT_NAME}.json 45 | VERBATIM 46 | ) 47 | 48 | # Pack bitstream 49 | add_custom_command( 50 | OUTPUT ${PROJECT_NAME}.bit 51 | COMMAND ${ICEPACK_CMD} ARGS ${ICEPACK_ARGS} 52 | DEPENDS ${PROJECT_NAME}.asc 53 | VERBATIM 54 | ) 55 | 56 | # Target to force synthesis 57 | add_custom_target(${TARGET_NAME}_synth ALL 58 | DEPENDS ${PROJECT_NAME}.bit 59 | ) 60 | 61 | # Programming 62 | add_custom_target(${TARGET_NAME}_flash 63 | DEPENDS ${PROJECT_NAME}.bit 64 | ) 65 | add_custom_command( 66 | TARGET ${TARGET_NAME}_flash 67 | COMMAND ${FPGA_PROG_CMD} ARGS -f ${PROJECT_NAME}.bit 68 | DEPENDS ${PROJECT_NAME}.bit 69 | VERBATIM 70 | ) 71 | 72 | endfunction() 73 | -------------------------------------------------------------------------------- /cmake/wishbone_intercon.cmake: -------------------------------------------------------------------------------- 1 | function(wishbone_gen) 2 | 3 | cmake_parse_arguments( 4 | CONF 5 | "" 6 | "CONFIG_FILE;MODULE_NAME;FOR_TARGET;PROJNAME" 7 | "" 8 | ${ARGN} 9 | ) 10 | 11 | find_program(GEN_CMD wb_intercon_gen2.py ${VENDOR_DIR}/wb_intercon/sw) 12 | 13 | set(CMDLINE ${CONF_CONFIG_FILE} "${CONF_MODULE_NAME}.v" "${CONF_MODULE_NAME}") 14 | 15 | add_custom_command( 16 | OUTPUT ${CONF_MODULE_NAME} 17 | COMMAND ${GEN_CMD} ARGS ${CMDLINE} 18 | DEPENDS ${CONF_CONFIG_FILE} 19 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/rtl/gen 20 | VERBATIM 21 | ) 22 | 23 | add_custom_target(${CONF_PROJNAME}_wishbone_gen_${CONF_MODULE_NAME} 24 | DEPENDS ${CONF_MODULE_NAME} 25 | ) 26 | 27 | add_dependencies(${CONF_FOR_TARGET} ${CONF_PROJNAME}_wishbone_gen_${CONF_MODULE_NAME}) 28 | 29 | endfunction() 30 | -------------------------------------------------------------------------------- /firmware/bootstrap.cpp: -------------------------------------------------------------------------------- 1 | extern unsigned _data_loadaddr, _data, _edata; 2 | 3 | typedef void (*void_fun)(void); 4 | extern void_fun __preinit_array_start, __preinit_array_end; 5 | extern void_fun __init_array_start, __init_array_end; 6 | extern void_fun __fini_array_start, __fini_array_end; 7 | 8 | extern "C" { 9 | int main(void); 10 | void reset_handler(void); 11 | } 12 | 13 | void reset_handler(void) { 14 | // Load the initialized .data section into place 15 | volatile unsigned *src, *dest; 16 | for (src = &_data_loadaddr, dest = &_data; dest < &_edata; src++, dest++) { 17 | *dest = *src; 18 | } 19 | 20 | // Handle C++ constructors / anything with __attribute__(constructor) 21 | void_fun *fp; 22 | for (fp = &__preinit_array_start; fp < &__preinit_array_end; fp++) { 23 | (*fp)(); 24 | } 25 | for (fp = &__init_array_start; fp < &__init_array_end; fp++) { 26 | (*fp)(); 27 | } 28 | 29 | // Invoke our actual application 30 | main(); 31 | 32 | // Run destructors 33 | for (fp = &__fini_array_start; fp < &__fini_array_end; fp++) { 34 | (*fp)(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /firmware/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | int main(void); 5 | void init(void); 6 | void loop(void); 7 | void blocking_handler(void); 8 | void timer_interrupt(void); 9 | } 10 | 11 | #define CPU_CLK_HZ 42'000'000 12 | 13 | #define MMIO32(ADDR) (*(volatile uint32_t *)(ADDR)) 14 | #define REG32(BASE, OFFSET) MMIO32(BASE + (OFFSET << 2)) 15 | 16 | // Timer for driving the machine timer interrupt 17 | // Note that since each register is 32 bits wide, subsequent register addresses 18 | // are 4 bytes apart 19 | #define TIMER_BASE 0x40000000 20 | #define TIMER_PRESCALER REG32(TIMER_BASE, 0x00) 21 | #define TIMER_FLAGS REG32(TIMER_BASE, 0x01) 22 | #define TIMER_FLAGS__PENDING (1 << 0) 23 | 24 | // Buffered UART 25 | #define UART_BASE 0x40001000 26 | // Baudrate prescaler counter 27 | #define UART_PRESCALER REG32(UART_BASE, 0x00) 28 | // Transmit block 29 | #define UART_TX_FLAGS REG32(UART_BASE, 0x10) 30 | #define UART_TX_BUFFER_COUNT REG32(UART_BASE, 0x11) 31 | #define UART_TX_BUFFER_FREE REG32(UART_BASE, 0x12) 32 | #define UART_TX_BUFFER_WRITE REG32(UART_BASE, 0x13) 33 | // Receive block 34 | #define UART_RX_FLAGS REG32(UART_BASE, 0x20) 35 | #define UART_RX_BUFFER_COUNT REG32(UART_BASE, 0x21) 36 | #define UART_RX_BUFFER_FREE REG32(UART_BASE, 0x22) 37 | #define UART_RX_BUFFER_READ REG32(UART_BASE, 0x23) 38 | 39 | // RGB LED 40 | #define LED_BASE 0x40002000 41 | #define LED_PWM_PRESCALER REG32(LED_BASE, 0) 42 | #define LED_BGR_DATA REG32(LED_BASE, 1) 43 | 44 | // Application code entrypoint 45 | int main(void) { 46 | init(); 47 | while (true) { 48 | loop(); 49 | } 50 | } 51 | 52 | // Since we don't necessarily want to define all of these interrupt handlers up 53 | // front, we will start by defining a simple blocking handler and pointing all 54 | // interrupts there. 55 | void blocking_handler(void) { 56 | while (1) { 57 | } 58 | } 59 | 60 | // Let's also create a simple timer interrupt handler 61 | volatile uint32_t time_ms = 0; 62 | void timer_interrupt(void) { 63 | // Increment the millisecond counter 64 | time_ms++; 65 | 66 | // We need to also clear the source of the interrupt, otherwise when we 67 | // return from interrupt it will just fire again right away. 68 | TIMER_FLAGS |= TIMER_FLAGS__PENDING; 69 | } 70 | 71 | // Create a type alias for our exception handlers, which are void functions 72 | typedef void (*isr_vector)(void); 73 | 74 | // The basic interrupts for RISC-V are the software, timer and external 75 | // interrupts, each of which is specified for the user, supervisor and machine 76 | // privilege levels. For clear naming, we will create a struct that matches the 77 | // order of the interrupt codes. 78 | struct { 79 | // Software interrupt 80 | isr_vector software_user_isr = &blocking_handler; 81 | isr_vector software_supervisor_isr = &blocking_handler; 82 | isr_vector software__reserved = &blocking_handler; 83 | isr_vector software_machine_isr = &blocking_handler; 84 | // Timer interrupt 85 | isr_vector timer_user_isr = &blocking_handler; 86 | isr_vector timer_supervisor_isr = &blocking_handler; 87 | isr_vector timer__reserved = &blocking_handler; 88 | isr_vector timer_machine_isr = &timer_interrupt; 89 | // External interrupt 90 | isr_vector external_user_isr = &blocking_handler; 91 | isr_vector external_supervisor_isr = &blocking_handler; 92 | isr_vector external__reserved = &blocking_handler; 93 | isr_vector external_machine_isr = &blocking_handler; 94 | } vector_table; 95 | 96 | // We need to decorate this function with __attribute__((interrupt)) so that 97 | // the compiler knows to save/restore all register state, as well as to 98 | // re-enable interrupts on return with the mret instruction. 99 | void __attribute__((interrupt)) interrupt_handler(void) { 100 | // When an interrupt occurs, the mcause register contains the interrupt type 101 | uint32_t mcause; 102 | asm volatile("csrr %0, mcause" : "=r"(mcause)); 103 | 104 | // The top bit of mcause is the sync vs async exception bit, we don't 105 | // handle that here so mask it off 106 | mcause &= 0x7FFFFFFF; 107 | 108 | // If the cause is some number out of range of our handler table, we have 109 | // no way to handle this interrupt! Block forever. 110 | if (mcause >= (sizeof(vector_table) / sizeof(isr_vector))) { 111 | while (true) { 112 | } 113 | } 114 | 115 | // Otherwise, we can jump to the handler listed in our vector table. 116 | // Since we took care to order our struct to match the interrupt IDs, we can 117 | // reinterpret it as an array for easy indexing based on mcause 118 | ((isr_vector *)&vector_table)[mcause](); 119 | } 120 | 121 | void init() { 122 | // Write that address into our mtvec register. 123 | // Since our address is 32-bit aligned, we are in non-vectored mode by default 124 | asm volatile("csrw mtvec, %0" ::"r"(&interrupt_handler)); 125 | 126 | // We now want to enable the machine timer interrupt. To do this, we need to 127 | // set bit 7 in the Machine Interrupt Enable (mie) register 128 | asm volatile("csrs mie, %0" ::"r"(1 << 7)); 129 | 130 | // We then need to enable machine interrupts globally, by setting bit 3 in the 131 | // Machine Status Register (mstatus). 132 | asm volatile("csrs mstatus, %0" ::"i"(1 << 3)); 133 | 134 | // Set our uart prescaler 135 | UART_PRESCALER = CPU_CLK_HZ / 2'000'000 - 1; 136 | 137 | // Set our timer counter prescaler 138 | TIMER_PRESCALER = CPU_CLK_HZ / 1'000 - 1; 139 | 140 | // 1kHz LED PWM with 256 counter states 141 | LED_PWM_PRESCALER = CPU_CLK_HZ / 256 / 1'000 - 1; 142 | } 143 | 144 | // Extremely basic integer formatting method 145 | // Uses a static internal buffer, so subsequent calls will invalidate the data 146 | const char *unsigned_to_str(uint32_t val) { 147 | static char out_buf[11]; 148 | // Max size of u32 is 4294967295, or 10 characters. 149 | // Plus one for null byte. 150 | int out_buf_idx = 11; 151 | out_buf[--out_buf_idx] = '\0'; 152 | while (val) { 153 | const unsigned digit = val % 10; 154 | val = val / 10; 155 | out_buf[--out_buf_idx] = '0' + digit; 156 | } 157 | return &out_buf[out_buf_idx]; 158 | } 159 | 160 | // Writes a string out to the uart. 161 | // Does not check that the uart buffer has enough space, so sending too much 162 | // data without either 163 | // - Checking the uart flags register for a fill condition or 164 | // - Altering the uart gateware to delay ACKs on writes until the buffer can 165 | // accomodate 166 | // will result in lost byes. 167 | void uart_write(const char *str) { 168 | while (*str) { 169 | UART_TX_BUFFER_WRITE = *str; 170 | str++; 171 | } 172 | } 173 | 174 | // Main application event loop. 175 | void loop() { 176 | // Check if it is time to print a log update 177 | static uint32_t last_uart_log = 0; 178 | if (time_ms - last_uart_log > 500) { 179 | // Update the last log timestamp 180 | last_uart_log = time_ms; 181 | 182 | // For fun, let's count how many CPU cycles it takes 183 | uint32_t cycle_start, cycle_end; 184 | // Read the current cycle counter 185 | asm volatile("rdcycle %0" : "=r"(cycle_start)); 186 | 187 | // Format the current time into a buffer 188 | const char *time_str = unsigned_to_str(time_ms); 189 | // Write out a status string to the uart 190 | uart_write("The current time is "); 191 | uart_write(time_str); 192 | uart_write("\r\n"); 193 | 194 | // Read the cycle counter again 195 | asm volatile("rdcycle %0" : "=r"(cycle_end)); 196 | 197 | // Print how long the first print took, in cycles 198 | const char *cycle_str = unsigned_to_str(cycle_end - cycle_start); 199 | // Write out a status string to the uart 200 | uart_write("Previous log took "); 201 | uart_write(cycle_str); 202 | uart_write(" cycles to execute\r\n"); 203 | } 204 | 205 | // Check if it's time to cycle our indicator LED 206 | static uint32_t last_led_update = 0; 207 | if (time_ms - last_led_update > 2) { 208 | // Update timer 209 | last_led_update = time_ms; 210 | 211 | // LED state 212 | static uint32_t led_data[3] = {0, 0, 0}; 213 | static int led_count_up = 1; 214 | static uint32_t led_index = 0; 215 | 216 | // Cycle each LED on and off in intensity, one after the other 217 | if (led_count_up) { 218 | led_data[led_index]++; 219 | if (led_data[led_index] >= 255) { 220 | led_data[led_index] = 255; 221 | led_count_up = 0; 222 | } 223 | } else { 224 | led_data[led_index]--; 225 | if (led_data[led_index] == 0) { 226 | led_count_up = 1; 227 | led_index = (led_index + 1) % 3; 228 | } 229 | } 230 | 231 | // Pack the LED data and update our PWM peripheral 232 | LED_BGR_DATA = ((led_data[0] & 0xFF) | ((led_data[1] & 0xFF) << 8) | 233 | ((led_data[2] & 0xFF) << 16)); 234 | } 235 | } 236 | 237 | -------------------------------------------------------------------------------- /firmware/sections.ld: -------------------------------------------------------------------------------- 1 | /* Define our main memory regions - we created two memory blocks, one to act as 2 | * RAM and one to contain our program (ROM). The address here should match the 3 | * address we gave the memories in our wishbone memory layout. 4 | */ 5 | MEMORY { 6 | ram (rwx) : ORIGIN = 0x80000000, LENGTH = 0x00000800 7 | rom (rx) : ORIGIN = 0x20000000, LENGTH = 0x00001000 8 | } 9 | 10 | /* Now we need to locate our sections within those memories. */ 11 | SECTIONS { 12 | /* Firstly, our text section. This will contain the program code, and any 13 | * other read-only data. This data is immutable, and we will locate it 14 | * inside our ROM block 15 | */ 16 | .text : { 17 | /* Ensure that our reset vector code is at the very beginning of ROM, 18 | * where our CPU will start execution 19 | */ 20 | *(.reset_vector*) 21 | /* Program code */ 22 | *(.text*) 23 | /* Ensure that the next block is aligned to a 32-bit word boundary */ 24 | . = ALIGN(4); 25 | /* Read-only data */ 26 | *(.rodata*) 27 | /* Ensure that the next block is aligned to a 32-bit word boundary */ 28 | } >rom /* Locate this group inside the ROM memory */ 29 | 30 | /* These next three groups are sections that contain a list of function 31 | * pointers, used by C++ code to perform static constructor/destructor calls 32 | * pre/post-main. 33 | * These are static memory regions, so we will locate them in ROM as well. 34 | */ 35 | .preinit_array : { 36 | . = ALIGN(4); 37 | /* Export a symbol with the address of the start of the preinit array. 38 | * This symbol, and the corresponding one at the end of the array, will 39 | * be used by our startup code to locate and iterate these functions 40 | */ 41 | __preinit_array_start = .; 42 | /* Insert the preinit array section, and force ld to KEEP it even if it 43 | * otherwise looks like it might be unused. 44 | */ 45 | KEEP (*(.preinit_array)) 46 | __preinit_array_end = .; 47 | } >rom 48 | .init_array : { 49 | . = ALIGN(4); 50 | __init_array_start = .; 51 | KEEP (*(SORT(.init_array.*))) 52 | KEEP (*(.init_array)) 53 | __init_array_end = .; 54 | } >rom 55 | .fini_array : { 56 | . = ALIGN(4); 57 | __fini_array_start = .; 58 | KEEP (*(.fini_array)) 59 | KEEP (*(SORT(.fini_array.*))) 60 | __fini_array_end = .; 61 | } >rom 62 | 63 | /* We have now reached the end of the read-only text segment. Export the 64 | * _etext symbol with this location in case it is needed. 65 | */ 66 | . = ALIGN(4); 67 | _etext = .; 68 | 69 | /* Now that we are done with our read-only sections, we can move on to RAM. 70 | * There are two sections we care about here: .data, which is initialized 71 | * variable memory, and .bss, which is zero-initialized memory. 72 | */ 73 | 74 | /* Our data segment is special in that it has both a location in rom (where 75 | * the data to be loaded into memory is stored) and in ram (where the data 76 | * must be copied to before main() is called). 77 | */ 78 | .data : { 79 | /* Export a symbol for the start of the data section */ 80 | _data = .; 81 | /* Insert our actual data */ 82 | *(.data*) 83 | . = ALIGN(4); 84 | /* Insert the small data section at the end, so that it is close to the 85 | * small bss section at the start of the next segment 86 | */ 87 | __global_pointer$ = . + 0x800; 88 | *(.sdata*) 89 | . = ALIGN(4); 90 | /* And also make a note of where the section ends */ 91 | _edata = .; 92 | /* This section is special in having a Load Memory Address (LMA) that is 93 | * different from the Virtual Memory Address (VMA). When the program is 94 | * executing, it will expect the data in this section to be located at the 95 | * VMA (in this case, in RAM). But since we need this data to be 96 | * initialized, and RAM is volatile, it must have a different location for 97 | * the data to be loaded _from_, the LMA. In our case, the LMA is inside the 98 | * non-volatile ROM segment. 99 | */ 100 | } >ram AT >rom /* VMA in ram, LMA in rom */ 101 | 102 | /* Since we will need to reference the LMA of the .data section in our 103 | * startup code (so that we may copy the data into ram), we need the linker 104 | * to export a symbol here with that value. 105 | */ 106 | _data_loadaddr = LOADADDR(.data); 107 | 108 | /* Finally, we get to our memory that doesn't require special 109 | * initialization 110 | */ 111 | .bss : { 112 | /* Insert the small .sbss section first, so that it is close to 113 | * the small data section 114 | */ 115 | *(.sbss*) 116 | . = ALIGN(4); 117 | /* Insert the .bss (zero-initialized) section */ 118 | *(.bss*) 119 | /* Also include the COMMON (uninitialized) section */ 120 | *(COMMON) 121 | . = ALIGN(4); 122 | _ebss = .; 123 | } >ram 124 | 125 | /* That's it for sections - for completeness, we also export a symbol at the 126 | * very end of our memory range. 127 | */ 128 | . = ALIGN(4); 129 | end = .; 130 | } 131 | 132 | /* Finally, we need to tell our startup code where to locate the stack. Since 133 | * the stack grows down, we export the stack start symbol to be at the end of 134 | * our RAM memory section. 135 | */ 136 | PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); 137 | -------------------------------------------------------------------------------- /firmware/start.s: -------------------------------------------------------------------------------- 1 | # Since we need to ensure that this is the very first code the CPU runs at 2 | # startup, we place it in a special reset vector section that we link before 3 | # anything else in the .text region 4 | .section .reset_vector 5 | 6 | # In order to initialize the stack pointer, we need to know where in memory 7 | # the stack begins. Our linker script will provide this symbol. 8 | .global _stack 9 | 10 | # Our main application entrypoint label 11 | start: 12 | 13 | # Initialize global pointer 14 | # Need to set norelax here, otherwise the optimizer might convert this to 15 | # mv gp, gp which wouldn't be very useful 16 | .option push 17 | .option norelax 18 | la gp, __global_pointer$ 19 | .option pop 20 | 21 | # Load the address of the _stack label into the stack pointer 22 | la sp, _stack 23 | 24 | # Once the register file is initialized and the stack pointer is set, we can 25 | # jump to our actual program entry point 26 | call reset_handler 27 | 28 | # If our reset handler ever returns, just keep the CPU in an infinite loop. 29 | loop: 30 | j loop 31 | -------------------------------------------------------------------------------- /hx4k_pmod.pcf: -------------------------------------------------------------------------------- 1 | # 12 MHz clock 2 | set_io CLK_12MHZ 21 3 | 4 | # RGB led 5 | set_io LED[0] 85 6 | set_io LED[1] 84 7 | set_io LED[2] 83 8 | 9 | # SPI connection to flash chip 10 | set_io FLASH_SPI_CS 71 11 | set_io FLASH_SPI_SCK 70 12 | set_io FLASH_SPI_MISO 68 13 | set_io FLASH_SPI_MOSI 67 14 | 15 | # SPI connection to microcontroller 16 | set_io MCU_SPI_CS 73 17 | set_io MCU_SPI_SCK 74 18 | set_io MCU_SPI_MISO 75 19 | set_io MCU_SPI_MOSI 76 20 | 21 | # UART connection to microcontroller 22 | # Note that these names are from the point of view of the MCU. 23 | # The FPGA should TRANSMIT on MCU_RX, and RECEIVE on MCU_TX 24 | set_io MCU_UART_RX 81 25 | set_io MCU_UART_TX 82 26 | 27 | ### PMODs 28 | 29 | # A1 30 | # Note that A1[3] and A1[6] map to the slice X16/Y0/io1, which is the same block 31 | # that houses the PLL on this device. If these pins reqyire tri-state support, 32 | # it will cause the PLL to fail to constrain. 33 | set_io PMOD_A1[0] 61 34 | set_io PMOD_A1[1] 56 35 | set_io PMOD_A1[2] 52 36 | set_io PMOD_A1[3] 48 37 | set_io PMOD_A1[4] 60 38 | set_io PMOD_A1[5] 55 39 | set_io PMOD_A1[6] 49 40 | set_io PMOD_A1[7] 47 41 | 42 | # A2 43 | set_io PMOD_A2[0] 45 44 | set_io PMOD_A2[1] 43 45 | set_io PMOD_A2[2] 41 46 | set_io PMOD_A2[3] 38 47 | set_io PMOD_A2[4] 44 48 | set_io PMOD_A2[5] 42 49 | set_io PMOD_A2[6] 39 50 | set_io PMOD_A2[7] 37 51 | 52 | # A3 53 | set_io PMOD_A3[0] 31 54 | set_io PMOD_A3[1] 28 55 | set_io PMOD_A3[2] 25 56 | set_io PMOD_A3[3] 23 57 | set_io PMOD_A3[4] 29 58 | set_io PMOD_A3[5] 26 59 | set_io PMOD_A3[6] 24 60 | set_io PMOD_A3[7] 22 61 | 62 | # A4 63 | set_io PMOD_A4[0] 20 64 | set_io PMOD_A4[1] 18 65 | set_io PMOD_A4[2] 16 66 | set_io PMOD_A4[3] 12 67 | set_io PMOD_A4[4] 19 68 | set_io PMOD_A4[5] 17 69 | set_io PMOD_A4[6] 15 70 | set_io PMOD_A4[7] 11 71 | 72 | # B1 73 | set_io PMOD_B1[0] 97 74 | set_io PMOD_B1[1] 95 75 | set_io PMOD_B1[2] 93 76 | set_io PMOD_B1[3] 90 77 | set_io PMOD_B1[4] 96 78 | set_io PMOD_B1[5] 94 79 | set_io PMOD_B1[6] 91 80 | set_io PMOD_B1[7] 88 81 | 82 | # B2 83 | set_io PMOD_B2[0] 107 84 | set_io PMOD_B2[1] 105 85 | set_io PMOD_B2[2] 102 86 | set_io PMOD_B2[3] 99 87 | set_io PMOD_B2[4] 106 88 | set_io PMOD_B2[5] 104 89 | set_io PMOD_B2[6] 101 90 | set_io PMOD_B2[7] 98 91 | 92 | # B3 93 | set_io PMOD_B3[0] 121 94 | set_io PMOD_B3[1] 119 95 | set_io PMOD_B3[2] 117 96 | set_io PMOD_B3[3] 115 97 | set_io PMOD_B3[4] 120 98 | set_io PMOD_B3[5] 118 99 | set_io PMOD_B3[6] 116 100 | set_io PMOD_B3[7] 114 101 | 102 | # B4 103 | set_io PMOD_B4[0] 135 104 | set_io PMOD_B4[1] 130 105 | set_io PMOD_B4[2] 128 106 | set_io PMOD_B4[3] 124 107 | set_io PMOD_B4[4] 134 108 | set_io PMOD_B4[5] 129 109 | set_io PMOD_B4[6] 125 110 | set_io PMOD_B4[7] 122 111 | 112 | # C1 113 | set_io PMOD_C1[0] 10 114 | set_io PMOD_C1[1] 8 115 | set_io PMOD_C1[2] 4 116 | set_io PMOD_C1[3] 2 117 | set_io PMOD_C1[4] 9 118 | set_io PMOD_C1[5] 7 119 | set_io PMOD_C1[6] 3 120 | set_io PMOD_C1[7] 1 121 | 122 | # C2 123 | set_io PMOD_C2[0] 144 124 | set_io PMOD_C2[1] 142 125 | set_io PMOD_C2[2] 139 126 | set_io PMOD_C2[3] 137 127 | set_io PMOD_C2[4] 143 128 | set_io PMOD_C2[5] 141 129 | set_io PMOD_C2[6] 138 130 | set_io PMOD_C2[7] 136 131 | -------------------------------------------------------------------------------- /img/soc_blink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rschlaikjer/fpga-3-softcores/6ccc8ac55f16ffcf17cef1d831714142ab64f27b/img/soc_blink.gif -------------------------------------------------------------------------------- /rtl/fifo.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module fifo( 4 | // RCC 5 | input wire i_reset, 6 | input wire i_clk, 7 | // Write 8 | input [DATA_WIDTH-1:0] i_w_data, 9 | input wire i_w_data_stb, 10 | // Read 11 | output wire [DATA_WIDTH-1:0] o_r_data, 12 | input wire i_r_data_stb, 13 | // Status 14 | output wire o_full, 15 | output wire o_empty, 16 | output wire [$clog2(MAX_ENTRIES)-1:0] o_item_count, 17 | output wire [$clog2(MAX_ENTRIES)-1:0] o_free_size 18 | ); 19 | 20 | // Width of FIFO entries 21 | parameter DATA_WIDTH = 16; 22 | // Element count for backing data array. 23 | // Note that this implementation can only actually hold MAX_ENTRIES - 1 entries 24 | parameter MAX_ENTRIES = 8; 25 | 26 | // Read/write pointers 27 | reg [$clog2(MAX_ENTRIES)-1:0] write_idx = 0; 28 | reg [$clog2(MAX_ENTRIES)-1:0] read_idx = 0; 29 | 30 | // Data storage 31 | reg [DATA_WIDTH-1:0] data [MAX_ENTRIES-1:0]; 32 | 33 | // Empty if read/write are same index 34 | wire [$clog2(MAX_ENTRIES)-1:0] read_plus_1 = read_idx + 1; 35 | reg r_empty = 1'b1; 36 | assign o_empty = r_empty; 37 | 38 | // Full if write + 1 == read 39 | wire [$clog2(MAX_ENTRIES)-1:0] write_plus_2 = write_idx + 2; 40 | reg r_full = 1'b0; 41 | assign o_full = r_full; 42 | 43 | // Number of items in FIFO 44 | reg [$clog2(MAX_ENTRIES)-1:0] r_item_count = 0; 45 | assign o_item_count = r_item_count; 46 | // Free size is max size - item_count - 1 47 | wire [$clog2(MAX_ENTRIES+1)-1:0] free_size = (MAX_ENTRIES-1) - r_item_count; 48 | assign o_free_size = free_size[$clog2(MAX_ENTRIES)-1:0]; 49 | 50 | // Buffer for read data from block ram 51 | // Need to always read this for it to interpret the read clock domain correctly 52 | reg [DATA_WIDTH-1:0] read_idx_data; 53 | 54 | // If a write occurs to an empty fifo, the r_empty signal will go low 55 | // immediately, but the output value will not change until the cycle after. 56 | // To get around this, check for this case and display the input directly on 57 | // the output when it happens 58 | reg is_write_on_empty; 59 | always @(posedge i_clk) 60 | is_write_on_empty <= (write_idx == read_idx && i_w_data_stb); 61 | assign o_r_data = is_write_on_empty ? i_w_data : read_idx_data; 62 | 63 | // Data 64 | always @(posedge i_clk) begin 65 | if (i_reset) begin 66 | write_idx <= 0; 67 | read_idx <= 0; 68 | end else begin 69 | // Read can always load the data at the read index into the output reg 70 | read_idx_data <= data[read_idx]; 71 | 72 | // Special case concurrent write + read - this works even if we are full 73 | // since both pointers will move 74 | if (!r_empty && i_w_data_stb && i_r_data_stb) begin 75 | data[write_idx] <= i_w_data; 76 | write_idx <= write_idx + 1; 77 | read_idx <= read_idx + 1; 78 | end else begin 79 | // Are we being written? 80 | if (i_w_data_stb && !r_full) begin 81 | // Set write enable on RAM 82 | data[write_idx] <= i_w_data; 83 | write_idx <= write_idx + 1; 84 | end 85 | 86 | // Are we being read? 87 | if (i_r_data_stb && !r_empty) begin 88 | read_idx <= read_idx + 1; 89 | end 90 | end 91 | 92 | end 93 | end 94 | 95 | // Size and flags 96 | always @(posedge i_clk) begin 97 | if (i_reset) begin 98 | r_item_count <= 0; 99 | r_full <= 0; 100 | r_empty <= 1; 101 | end else begin 102 | casez ({i_w_data_stb, i_r_data_stb, r_empty, r_full}) 103 | 4'b010?: begin 104 | // Read while non-empty 105 | r_item_count <= r_item_count - 1; 106 | r_empty <= read_plus_1 == write_idx; 107 | r_full <= 0; // Can't be full if we just read something 108 | end 109 | 4'b10?0: begin 110 | // Write while non-full 111 | r_item_count <= r_item_count + 1; 112 | r_full <= write_plus_2 == read_idx; 113 | r_empty <= 0; // Can't be empty if we just wrote something 114 | end 115 | default: begin /* anything else doesn't affect */ end 116 | endcase 117 | end 118 | end 119 | 120 | endmodule 121 | -------------------------------------------------------------------------------- /rtl/gen/soc_wb.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | filesets: 3 | rtl: 4 | files: 5 | - wb_intercon.v: {file_type: verilogSource} 6 | - wb_intercon.vh: {file_type: verilogSource, is_include_file: true} 7 | name: ::soc_wb 8 | targets: 9 | default: 10 | filesets: [rtl] 11 | -------------------------------------------------------------------------------- /rtl/gen/wb_intercon.v: -------------------------------------------------------------------------------- 1 | // THIS FILE IS AUTOGENERATED BY wb_intercon_gen 2 | // ANY MANUAL CHANGES WILL BE LOST 3 | module wb_intercon 4 | (input wb_clk_i, 5 | input wb_rst_i, 6 | input [31:0] wb_cpu0_ibus_adr_i, 7 | input [31:0] wb_cpu0_ibus_dat_i, 8 | input [3:0] wb_cpu0_ibus_sel_i, 9 | input wb_cpu0_ibus_we_i, 10 | input wb_cpu0_ibus_cyc_i, 11 | input wb_cpu0_ibus_stb_i, 12 | input [2:0] wb_cpu0_ibus_cti_i, 13 | input [1:0] wb_cpu0_ibus_bte_i, 14 | output [31:0] wb_cpu0_ibus_dat_o, 15 | output wb_cpu0_ibus_ack_o, 16 | output wb_cpu0_ibus_err_o, 17 | output wb_cpu0_ibus_rty_o, 18 | input [31:0] wb_cpu0_dbus_adr_i, 19 | input [31:0] wb_cpu0_dbus_dat_i, 20 | input [3:0] wb_cpu0_dbus_sel_i, 21 | input wb_cpu0_dbus_we_i, 22 | input wb_cpu0_dbus_cyc_i, 23 | input wb_cpu0_dbus_stb_i, 24 | input [2:0] wb_cpu0_dbus_cti_i, 25 | input [1:0] wb_cpu0_dbus_bte_i, 26 | output [31:0] wb_cpu0_dbus_dat_o, 27 | output wb_cpu0_dbus_ack_o, 28 | output wb_cpu0_dbus_err_o, 29 | output wb_cpu0_dbus_rty_o, 30 | output [31:0] wb_timer0_adr_o, 31 | output [31:0] wb_timer0_dat_o, 32 | output [3:0] wb_timer0_sel_o, 33 | output wb_timer0_we_o, 34 | output wb_timer0_cyc_o, 35 | output wb_timer0_stb_o, 36 | output [2:0] wb_timer0_cti_o, 37 | output [1:0] wb_timer0_bte_o, 38 | input [31:0] wb_timer0_dat_i, 39 | input wb_timer0_ack_i, 40 | input wb_timer0_err_i, 41 | input wb_timer0_rty_i, 42 | output [31:0] wb_uart0_adr_o, 43 | output [31:0] wb_uart0_dat_o, 44 | output [3:0] wb_uart0_sel_o, 45 | output wb_uart0_we_o, 46 | output wb_uart0_cyc_o, 47 | output wb_uart0_stb_o, 48 | output [2:0] wb_uart0_cti_o, 49 | output [1:0] wb_uart0_bte_o, 50 | input [31:0] wb_uart0_dat_i, 51 | input wb_uart0_ack_i, 52 | input wb_uart0_err_i, 53 | input wb_uart0_rty_i, 54 | output [31:0] wb_led0_adr_o, 55 | output [31:0] wb_led0_dat_o, 56 | output [3:0] wb_led0_sel_o, 57 | output wb_led0_we_o, 58 | output wb_led0_cyc_o, 59 | output wb_led0_stb_o, 60 | output [2:0] wb_led0_cti_o, 61 | output [1:0] wb_led0_bte_o, 62 | input [31:0] wb_led0_dat_i, 63 | input wb_led0_ack_i, 64 | input wb_led0_err_i, 65 | input wb_led0_rty_i, 66 | output [31:0] wb_cpu0_rom_adr_o, 67 | output [31:0] wb_cpu0_rom_dat_o, 68 | output [3:0] wb_cpu0_rom_sel_o, 69 | output wb_cpu0_rom_we_o, 70 | output wb_cpu0_rom_cyc_o, 71 | output wb_cpu0_rom_stb_o, 72 | output [2:0] wb_cpu0_rom_cti_o, 73 | output [1:0] wb_cpu0_rom_bte_o, 74 | input [31:0] wb_cpu0_rom_dat_i, 75 | input wb_cpu0_rom_ack_i, 76 | input wb_cpu0_rom_err_i, 77 | input wb_cpu0_rom_rty_i, 78 | output [31:0] wb_cpu0_ram_adr_o, 79 | output [31:0] wb_cpu0_ram_dat_o, 80 | output [3:0] wb_cpu0_ram_sel_o, 81 | output wb_cpu0_ram_we_o, 82 | output wb_cpu0_ram_cyc_o, 83 | output wb_cpu0_ram_stb_o, 84 | output [2:0] wb_cpu0_ram_cti_o, 85 | output [1:0] wb_cpu0_ram_bte_o, 86 | input [31:0] wb_cpu0_ram_dat_i, 87 | input wb_cpu0_ram_ack_i, 88 | input wb_cpu0_ram_err_i, 89 | input wb_cpu0_ram_rty_i); 90 | 91 | wire [31:0] wb_m2s_cpu0_ibus_cpu0_rom_adr; 92 | wire [31:0] wb_m2s_cpu0_ibus_cpu0_rom_dat; 93 | wire [3:0] wb_m2s_cpu0_ibus_cpu0_rom_sel; 94 | wire wb_m2s_cpu0_ibus_cpu0_rom_we; 95 | wire wb_m2s_cpu0_ibus_cpu0_rom_cyc; 96 | wire wb_m2s_cpu0_ibus_cpu0_rom_stb; 97 | wire [2:0] wb_m2s_cpu0_ibus_cpu0_rom_cti; 98 | wire [1:0] wb_m2s_cpu0_ibus_cpu0_rom_bte; 99 | wire [31:0] wb_s2m_cpu0_ibus_cpu0_rom_dat; 100 | wire wb_s2m_cpu0_ibus_cpu0_rom_ack; 101 | wire wb_s2m_cpu0_ibus_cpu0_rom_err; 102 | wire wb_s2m_cpu0_ibus_cpu0_rom_rty; 103 | wire [31:0] wb_m2s_cpu0_dbus_cpu0_rom_adr; 104 | wire [31:0] wb_m2s_cpu0_dbus_cpu0_rom_dat; 105 | wire [3:0] wb_m2s_cpu0_dbus_cpu0_rom_sel; 106 | wire wb_m2s_cpu0_dbus_cpu0_rom_we; 107 | wire wb_m2s_cpu0_dbus_cpu0_rom_cyc; 108 | wire wb_m2s_cpu0_dbus_cpu0_rom_stb; 109 | wire [2:0] wb_m2s_cpu0_dbus_cpu0_rom_cti; 110 | wire [1:0] wb_m2s_cpu0_dbus_cpu0_rom_bte; 111 | wire [31:0] wb_s2m_cpu0_dbus_cpu0_rom_dat; 112 | wire wb_s2m_cpu0_dbus_cpu0_rom_ack; 113 | wire wb_s2m_cpu0_dbus_cpu0_rom_err; 114 | wire wb_s2m_cpu0_dbus_cpu0_rom_rty; 115 | 116 | wb_mux 117 | #(.num_slaves (1), 118 | .MATCH_ADDR ({32'h20000000}), 119 | .MATCH_MASK ({32'hfffff000})) 120 | wb_mux_cpu0_ibus 121 | (.wb_clk_i (wb_clk_i), 122 | .wb_rst_i (wb_rst_i), 123 | .wbm_adr_i (wb_cpu0_ibus_adr_i), 124 | .wbm_dat_i (wb_cpu0_ibus_dat_i), 125 | .wbm_sel_i (wb_cpu0_ibus_sel_i), 126 | .wbm_we_i (wb_cpu0_ibus_we_i), 127 | .wbm_cyc_i (wb_cpu0_ibus_cyc_i), 128 | .wbm_stb_i (wb_cpu0_ibus_stb_i), 129 | .wbm_cti_i (wb_cpu0_ibus_cti_i), 130 | .wbm_bte_i (wb_cpu0_ibus_bte_i), 131 | .wbm_dat_o (wb_cpu0_ibus_dat_o), 132 | .wbm_ack_o (wb_cpu0_ibus_ack_o), 133 | .wbm_err_o (wb_cpu0_ibus_err_o), 134 | .wbm_rty_o (wb_cpu0_ibus_rty_o), 135 | .wbs_adr_o ({wb_m2s_cpu0_ibus_cpu0_rom_adr}), 136 | .wbs_dat_o ({wb_m2s_cpu0_ibus_cpu0_rom_dat}), 137 | .wbs_sel_o ({wb_m2s_cpu0_ibus_cpu0_rom_sel}), 138 | .wbs_we_o ({wb_m2s_cpu0_ibus_cpu0_rom_we}), 139 | .wbs_cyc_o ({wb_m2s_cpu0_ibus_cpu0_rom_cyc}), 140 | .wbs_stb_o ({wb_m2s_cpu0_ibus_cpu0_rom_stb}), 141 | .wbs_cti_o ({wb_m2s_cpu0_ibus_cpu0_rom_cti}), 142 | .wbs_bte_o ({wb_m2s_cpu0_ibus_cpu0_rom_bte}), 143 | .wbs_dat_i ({wb_s2m_cpu0_ibus_cpu0_rom_dat}), 144 | .wbs_ack_i ({wb_s2m_cpu0_ibus_cpu0_rom_ack}), 145 | .wbs_err_i ({wb_s2m_cpu0_ibus_cpu0_rom_err}), 146 | .wbs_rty_i ({wb_s2m_cpu0_ibus_cpu0_rom_rty})); 147 | 148 | wb_mux 149 | #(.num_slaves (5), 150 | .MATCH_ADDR ({32'h20000000, 32'h80000000, 32'h40000000, 32'h40001000, 32'h40002000}), 151 | .MATCH_MASK ({32'hfffff000, 32'hfffff800, 32'hfffff000, 32'hfffff000, 32'hfffff000})) 152 | wb_mux_cpu0_dbus 153 | (.wb_clk_i (wb_clk_i), 154 | .wb_rst_i (wb_rst_i), 155 | .wbm_adr_i (wb_cpu0_dbus_adr_i), 156 | .wbm_dat_i (wb_cpu0_dbus_dat_i), 157 | .wbm_sel_i (wb_cpu0_dbus_sel_i), 158 | .wbm_we_i (wb_cpu0_dbus_we_i), 159 | .wbm_cyc_i (wb_cpu0_dbus_cyc_i), 160 | .wbm_stb_i (wb_cpu0_dbus_stb_i), 161 | .wbm_cti_i (wb_cpu0_dbus_cti_i), 162 | .wbm_bte_i (wb_cpu0_dbus_bte_i), 163 | .wbm_dat_o (wb_cpu0_dbus_dat_o), 164 | .wbm_ack_o (wb_cpu0_dbus_ack_o), 165 | .wbm_err_o (wb_cpu0_dbus_err_o), 166 | .wbm_rty_o (wb_cpu0_dbus_rty_o), 167 | .wbs_adr_o ({wb_m2s_cpu0_dbus_cpu0_rom_adr, wb_cpu0_ram_adr_o, wb_timer0_adr_o, wb_uart0_adr_o, wb_led0_adr_o}), 168 | .wbs_dat_o ({wb_m2s_cpu0_dbus_cpu0_rom_dat, wb_cpu0_ram_dat_o, wb_timer0_dat_o, wb_uart0_dat_o, wb_led0_dat_o}), 169 | .wbs_sel_o ({wb_m2s_cpu0_dbus_cpu0_rom_sel, wb_cpu0_ram_sel_o, wb_timer0_sel_o, wb_uart0_sel_o, wb_led0_sel_o}), 170 | .wbs_we_o ({wb_m2s_cpu0_dbus_cpu0_rom_we, wb_cpu0_ram_we_o, wb_timer0_we_o, wb_uart0_we_o, wb_led0_we_o}), 171 | .wbs_cyc_o ({wb_m2s_cpu0_dbus_cpu0_rom_cyc, wb_cpu0_ram_cyc_o, wb_timer0_cyc_o, wb_uart0_cyc_o, wb_led0_cyc_o}), 172 | .wbs_stb_o ({wb_m2s_cpu0_dbus_cpu0_rom_stb, wb_cpu0_ram_stb_o, wb_timer0_stb_o, wb_uart0_stb_o, wb_led0_stb_o}), 173 | .wbs_cti_o ({wb_m2s_cpu0_dbus_cpu0_rom_cti, wb_cpu0_ram_cti_o, wb_timer0_cti_o, wb_uart0_cti_o, wb_led0_cti_o}), 174 | .wbs_bte_o ({wb_m2s_cpu0_dbus_cpu0_rom_bte, wb_cpu0_ram_bte_o, wb_timer0_bte_o, wb_uart0_bte_o, wb_led0_bte_o}), 175 | .wbs_dat_i ({wb_s2m_cpu0_dbus_cpu0_rom_dat, wb_cpu0_ram_dat_i, wb_timer0_dat_i, wb_uart0_dat_i, wb_led0_dat_i}), 176 | .wbs_ack_i ({wb_s2m_cpu0_dbus_cpu0_rom_ack, wb_cpu0_ram_ack_i, wb_timer0_ack_i, wb_uart0_ack_i, wb_led0_ack_i}), 177 | .wbs_err_i ({wb_s2m_cpu0_dbus_cpu0_rom_err, wb_cpu0_ram_err_i, wb_timer0_err_i, wb_uart0_err_i, wb_led0_err_i}), 178 | .wbs_rty_i ({wb_s2m_cpu0_dbus_cpu0_rom_rty, wb_cpu0_ram_rty_i, wb_timer0_rty_i, wb_uart0_rty_i, wb_led0_rty_i})); 179 | 180 | wb_arbiter 181 | #(.num_masters (2)) 182 | wb_arbiter_cpu0_rom 183 | (.wb_clk_i (wb_clk_i), 184 | .wb_rst_i (wb_rst_i), 185 | .wbm_adr_i ({wb_m2s_cpu0_ibus_cpu0_rom_adr, wb_m2s_cpu0_dbus_cpu0_rom_adr}), 186 | .wbm_dat_i ({wb_m2s_cpu0_ibus_cpu0_rom_dat, wb_m2s_cpu0_dbus_cpu0_rom_dat}), 187 | .wbm_sel_i ({wb_m2s_cpu0_ibus_cpu0_rom_sel, wb_m2s_cpu0_dbus_cpu0_rom_sel}), 188 | .wbm_we_i ({wb_m2s_cpu0_ibus_cpu0_rom_we, wb_m2s_cpu0_dbus_cpu0_rom_we}), 189 | .wbm_cyc_i ({wb_m2s_cpu0_ibus_cpu0_rom_cyc, wb_m2s_cpu0_dbus_cpu0_rom_cyc}), 190 | .wbm_stb_i ({wb_m2s_cpu0_ibus_cpu0_rom_stb, wb_m2s_cpu0_dbus_cpu0_rom_stb}), 191 | .wbm_cti_i ({wb_m2s_cpu0_ibus_cpu0_rom_cti, wb_m2s_cpu0_dbus_cpu0_rom_cti}), 192 | .wbm_bte_i ({wb_m2s_cpu0_ibus_cpu0_rom_bte, wb_m2s_cpu0_dbus_cpu0_rom_bte}), 193 | .wbm_dat_o ({wb_s2m_cpu0_ibus_cpu0_rom_dat, wb_s2m_cpu0_dbus_cpu0_rom_dat}), 194 | .wbm_ack_o ({wb_s2m_cpu0_ibus_cpu0_rom_ack, wb_s2m_cpu0_dbus_cpu0_rom_ack}), 195 | .wbm_err_o ({wb_s2m_cpu0_ibus_cpu0_rom_err, wb_s2m_cpu0_dbus_cpu0_rom_err}), 196 | .wbm_rty_o ({wb_s2m_cpu0_ibus_cpu0_rom_rty, wb_s2m_cpu0_dbus_cpu0_rom_rty}), 197 | .wbs_adr_o (wb_cpu0_rom_adr_o), 198 | .wbs_dat_o (wb_cpu0_rom_dat_o), 199 | .wbs_sel_o (wb_cpu0_rom_sel_o), 200 | .wbs_we_o (wb_cpu0_rom_we_o), 201 | .wbs_cyc_o (wb_cpu0_rom_cyc_o), 202 | .wbs_stb_o (wb_cpu0_rom_stb_o), 203 | .wbs_cti_o (wb_cpu0_rom_cti_o), 204 | .wbs_bte_o (wb_cpu0_rom_bte_o), 205 | .wbs_dat_i (wb_cpu0_rom_dat_i), 206 | .wbs_ack_i (wb_cpu0_rom_ack_i), 207 | .wbs_err_i (wb_cpu0_rom_err_i), 208 | .wbs_rty_i (wb_cpu0_rom_rty_i)); 209 | 210 | endmodule 211 | -------------------------------------------------------------------------------- /rtl/gen/wb_intercon.vh: -------------------------------------------------------------------------------- 1 | // THIS FILE IS AUTOGENERATED BY wb_intercon_gen 2 | // ANY MANUAL CHANGES WILL BE LOST 3 | wire [31:0] wb_m2s_cpu0_ibus_adr; 4 | wire [31:0] wb_m2s_cpu0_ibus_dat; 5 | wire [3:0] wb_m2s_cpu0_ibus_sel; 6 | wire wb_m2s_cpu0_ibus_we; 7 | wire wb_m2s_cpu0_ibus_cyc; 8 | wire wb_m2s_cpu0_ibus_stb; 9 | wire [2:0] wb_m2s_cpu0_ibus_cti; 10 | wire [1:0] wb_m2s_cpu0_ibus_bte; 11 | wire [31:0] wb_s2m_cpu0_ibus_dat; 12 | wire wb_s2m_cpu0_ibus_ack; 13 | wire wb_s2m_cpu0_ibus_err; 14 | wire wb_s2m_cpu0_ibus_rty; 15 | wire [31:0] wb_m2s_cpu0_dbus_adr; 16 | wire [31:0] wb_m2s_cpu0_dbus_dat; 17 | wire [3:0] wb_m2s_cpu0_dbus_sel; 18 | wire wb_m2s_cpu0_dbus_we; 19 | wire wb_m2s_cpu0_dbus_cyc; 20 | wire wb_m2s_cpu0_dbus_stb; 21 | wire [2:0] wb_m2s_cpu0_dbus_cti; 22 | wire [1:0] wb_m2s_cpu0_dbus_bte; 23 | wire [31:0] wb_s2m_cpu0_dbus_dat; 24 | wire wb_s2m_cpu0_dbus_ack; 25 | wire wb_s2m_cpu0_dbus_err; 26 | wire wb_s2m_cpu0_dbus_rty; 27 | wire [31:0] wb_m2s_timer0_adr; 28 | wire [31:0] wb_m2s_timer0_dat; 29 | wire [3:0] wb_m2s_timer0_sel; 30 | wire wb_m2s_timer0_we; 31 | wire wb_m2s_timer0_cyc; 32 | wire wb_m2s_timer0_stb; 33 | wire [2:0] wb_m2s_timer0_cti; 34 | wire [1:0] wb_m2s_timer0_bte; 35 | wire [31:0] wb_s2m_timer0_dat; 36 | wire wb_s2m_timer0_ack; 37 | wire wb_s2m_timer0_err; 38 | wire wb_s2m_timer0_rty; 39 | wire [31:0] wb_m2s_uart0_adr; 40 | wire [31:0] wb_m2s_uart0_dat; 41 | wire [3:0] wb_m2s_uart0_sel; 42 | wire wb_m2s_uart0_we; 43 | wire wb_m2s_uart0_cyc; 44 | wire wb_m2s_uart0_stb; 45 | wire [2:0] wb_m2s_uart0_cti; 46 | wire [1:0] wb_m2s_uart0_bte; 47 | wire [31:0] wb_s2m_uart0_dat; 48 | wire wb_s2m_uart0_ack; 49 | wire wb_s2m_uart0_err; 50 | wire wb_s2m_uart0_rty; 51 | wire [31:0] wb_m2s_led0_adr; 52 | wire [31:0] wb_m2s_led0_dat; 53 | wire [3:0] wb_m2s_led0_sel; 54 | wire wb_m2s_led0_we; 55 | wire wb_m2s_led0_cyc; 56 | wire wb_m2s_led0_stb; 57 | wire [2:0] wb_m2s_led0_cti; 58 | wire [1:0] wb_m2s_led0_bte; 59 | wire [31:0] wb_s2m_led0_dat; 60 | wire wb_s2m_led0_ack; 61 | wire wb_s2m_led0_err; 62 | wire wb_s2m_led0_rty; 63 | wire [31:0] wb_m2s_cpu0_rom_adr; 64 | wire [31:0] wb_m2s_cpu0_rom_dat; 65 | wire [3:0] wb_m2s_cpu0_rom_sel; 66 | wire wb_m2s_cpu0_rom_we; 67 | wire wb_m2s_cpu0_rom_cyc; 68 | wire wb_m2s_cpu0_rom_stb; 69 | wire [2:0] wb_m2s_cpu0_rom_cti; 70 | wire [1:0] wb_m2s_cpu0_rom_bte; 71 | wire [31:0] wb_s2m_cpu0_rom_dat; 72 | wire wb_s2m_cpu0_rom_ack; 73 | wire wb_s2m_cpu0_rom_err; 74 | wire wb_s2m_cpu0_rom_rty; 75 | wire [31:0] wb_m2s_cpu0_ram_adr; 76 | wire [31:0] wb_m2s_cpu0_ram_dat; 77 | wire [3:0] wb_m2s_cpu0_ram_sel; 78 | wire wb_m2s_cpu0_ram_we; 79 | wire wb_m2s_cpu0_ram_cyc; 80 | wire wb_m2s_cpu0_ram_stb; 81 | wire [2:0] wb_m2s_cpu0_ram_cti; 82 | wire [1:0] wb_m2s_cpu0_ram_bte; 83 | wire [31:0] wb_s2m_cpu0_ram_dat; 84 | wire wb_s2m_cpu0_ram_ack; 85 | wire wb_s2m_cpu0_ram_err; 86 | wire wb_s2m_cpu0_ram_rty; 87 | 88 | wb_intercon wb_intercon0 89 | (.wb_clk_i (wb_clk), 90 | .wb_rst_i (wb_rst), 91 | .wb_cpu0_ibus_adr_i (wb_m2s_cpu0_ibus_adr), 92 | .wb_cpu0_ibus_dat_i (wb_m2s_cpu0_ibus_dat), 93 | .wb_cpu0_ibus_sel_i (wb_m2s_cpu0_ibus_sel), 94 | .wb_cpu0_ibus_we_i (wb_m2s_cpu0_ibus_we), 95 | .wb_cpu0_ibus_cyc_i (wb_m2s_cpu0_ibus_cyc), 96 | .wb_cpu0_ibus_stb_i (wb_m2s_cpu0_ibus_stb), 97 | .wb_cpu0_ibus_cti_i (wb_m2s_cpu0_ibus_cti), 98 | .wb_cpu0_ibus_bte_i (wb_m2s_cpu0_ibus_bte), 99 | .wb_cpu0_ibus_dat_o (wb_s2m_cpu0_ibus_dat), 100 | .wb_cpu0_ibus_ack_o (wb_s2m_cpu0_ibus_ack), 101 | .wb_cpu0_ibus_err_o (wb_s2m_cpu0_ibus_err), 102 | .wb_cpu0_ibus_rty_o (wb_s2m_cpu0_ibus_rty), 103 | .wb_cpu0_dbus_adr_i (wb_m2s_cpu0_dbus_adr), 104 | .wb_cpu0_dbus_dat_i (wb_m2s_cpu0_dbus_dat), 105 | .wb_cpu0_dbus_sel_i (wb_m2s_cpu0_dbus_sel), 106 | .wb_cpu0_dbus_we_i (wb_m2s_cpu0_dbus_we), 107 | .wb_cpu0_dbus_cyc_i (wb_m2s_cpu0_dbus_cyc), 108 | .wb_cpu0_dbus_stb_i (wb_m2s_cpu0_dbus_stb), 109 | .wb_cpu0_dbus_cti_i (wb_m2s_cpu0_dbus_cti), 110 | .wb_cpu0_dbus_bte_i (wb_m2s_cpu0_dbus_bte), 111 | .wb_cpu0_dbus_dat_o (wb_s2m_cpu0_dbus_dat), 112 | .wb_cpu0_dbus_ack_o (wb_s2m_cpu0_dbus_ack), 113 | .wb_cpu0_dbus_err_o (wb_s2m_cpu0_dbus_err), 114 | .wb_cpu0_dbus_rty_o (wb_s2m_cpu0_dbus_rty), 115 | .wb_timer0_adr_o (wb_m2s_timer0_adr), 116 | .wb_timer0_dat_o (wb_m2s_timer0_dat), 117 | .wb_timer0_sel_o (wb_m2s_timer0_sel), 118 | .wb_timer0_we_o (wb_m2s_timer0_we), 119 | .wb_timer0_cyc_o (wb_m2s_timer0_cyc), 120 | .wb_timer0_stb_o (wb_m2s_timer0_stb), 121 | .wb_timer0_cti_o (wb_m2s_timer0_cti), 122 | .wb_timer0_bte_o (wb_m2s_timer0_bte), 123 | .wb_timer0_dat_i (wb_s2m_timer0_dat), 124 | .wb_timer0_ack_i (wb_s2m_timer0_ack), 125 | .wb_timer0_err_i (wb_s2m_timer0_err), 126 | .wb_timer0_rty_i (wb_s2m_timer0_rty), 127 | .wb_uart0_adr_o (wb_m2s_uart0_adr), 128 | .wb_uart0_dat_o (wb_m2s_uart0_dat), 129 | .wb_uart0_sel_o (wb_m2s_uart0_sel), 130 | .wb_uart0_we_o (wb_m2s_uart0_we), 131 | .wb_uart0_cyc_o (wb_m2s_uart0_cyc), 132 | .wb_uart0_stb_o (wb_m2s_uart0_stb), 133 | .wb_uart0_cti_o (wb_m2s_uart0_cti), 134 | .wb_uart0_bte_o (wb_m2s_uart0_bte), 135 | .wb_uart0_dat_i (wb_s2m_uart0_dat), 136 | .wb_uart0_ack_i (wb_s2m_uart0_ack), 137 | .wb_uart0_err_i (wb_s2m_uart0_err), 138 | .wb_uart0_rty_i (wb_s2m_uart0_rty), 139 | .wb_led0_adr_o (wb_m2s_led0_adr), 140 | .wb_led0_dat_o (wb_m2s_led0_dat), 141 | .wb_led0_sel_o (wb_m2s_led0_sel), 142 | .wb_led0_we_o (wb_m2s_led0_we), 143 | .wb_led0_cyc_o (wb_m2s_led0_cyc), 144 | .wb_led0_stb_o (wb_m2s_led0_stb), 145 | .wb_led0_cti_o (wb_m2s_led0_cti), 146 | .wb_led0_bte_o (wb_m2s_led0_bte), 147 | .wb_led0_dat_i (wb_s2m_led0_dat), 148 | .wb_led0_ack_i (wb_s2m_led0_ack), 149 | .wb_led0_err_i (wb_s2m_led0_err), 150 | .wb_led0_rty_i (wb_s2m_led0_rty), 151 | .wb_cpu0_rom_adr_o (wb_m2s_cpu0_rom_adr), 152 | .wb_cpu0_rom_dat_o (wb_m2s_cpu0_rom_dat), 153 | .wb_cpu0_rom_sel_o (wb_m2s_cpu0_rom_sel), 154 | .wb_cpu0_rom_we_o (wb_m2s_cpu0_rom_we), 155 | .wb_cpu0_rom_cyc_o (wb_m2s_cpu0_rom_cyc), 156 | .wb_cpu0_rom_stb_o (wb_m2s_cpu0_rom_stb), 157 | .wb_cpu0_rom_cti_o (wb_m2s_cpu0_rom_cti), 158 | .wb_cpu0_rom_bte_o (wb_m2s_cpu0_rom_bte), 159 | .wb_cpu0_rom_dat_i (wb_s2m_cpu0_rom_dat), 160 | .wb_cpu0_rom_ack_i (wb_s2m_cpu0_rom_ack), 161 | .wb_cpu0_rom_err_i (wb_s2m_cpu0_rom_err), 162 | .wb_cpu0_rom_rty_i (wb_s2m_cpu0_rom_rty), 163 | .wb_cpu0_ram_adr_o (wb_m2s_cpu0_ram_adr), 164 | .wb_cpu0_ram_dat_o (wb_m2s_cpu0_ram_dat), 165 | .wb_cpu0_ram_sel_o (wb_m2s_cpu0_ram_sel), 166 | .wb_cpu0_ram_we_o (wb_m2s_cpu0_ram_we), 167 | .wb_cpu0_ram_cyc_o (wb_m2s_cpu0_ram_cyc), 168 | .wb_cpu0_ram_stb_o (wb_m2s_cpu0_ram_stb), 169 | .wb_cpu0_ram_cti_o (wb_m2s_cpu0_ram_cti), 170 | .wb_cpu0_ram_bte_o (wb_m2s_cpu0_ram_bte), 171 | .wb_cpu0_ram_dat_i (wb_s2m_cpu0_ram_dat), 172 | .wb_cpu0_ram_ack_i (wb_s2m_cpu0_ram_ack), 173 | .wb_cpu0_ram_err_i (wb_s2m_cpu0_ram_err), 174 | .wb_cpu0_ram_rty_i (wb_s2m_cpu0_ram_rty)); 175 | 176 | -------------------------------------------------------------------------------- /rtl/ice_pll.v: -------------------------------------------------------------------------------- 1 | /** 2 | * PLL configuration 3 | * 4 | * This Verilog module was generated automatically 5 | * using the icepll tool from the IceStorm project. 6 | * Use at your own risk. 7 | * 8 | * Given input frequency: 12.000 MHz 9 | * Requested output frequency: 42.000 MHz 10 | * Achieved output frequency: 42.000 MHz 11 | */ 12 | 13 | module ice_pll( 14 | input clock_in, 15 | output clock_out, 16 | output locked 17 | ); 18 | `ifdef VERILATOR 19 | assign clock_out = clock_in; 20 | assign locked = 1'b1; 21 | `else 22 | SB_PLL40_CORE #( 23 | .FEEDBACK_PATH("SIMPLE"), 24 | .DIVR(4'b0000), // DIVR = 0 25 | .DIVF(7'b0110111), // DIVF = 55 26 | .DIVQ(3'b100), // DIVQ = 4 27 | .FILTER_RANGE(3'b001) // FILTER_RANGE = 1 28 | ) uut ( 29 | .LOCK(locked), 30 | .RESETB(1'b1), 31 | .BYPASS(1'b0), 32 | .REFERENCECLK(clock_in), 33 | .PLLOUTCORE(clock_out) 34 | ); 35 | `endif 36 | endmodule 37 | -------------------------------------------------------------------------------- /rtl/rgb_led_wb.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module rgb_led_wb( 4 | // RCC 5 | input wire i_clk, 6 | input wire i_reset, 7 | // Output BGR signal 8 | output reg [2:0] o_led_bgr, 9 | // Wishbone 10 | input wire [31:0] i_wb_adr, 11 | input wire [31:0] i_wb_dat, 12 | input wire [3:0] i_wb_sel, 13 | input wire i_wb_we, 14 | input wire i_wb_cyc, 15 | input wire i_wb_stb, 16 | output reg [31:0] o_wb_dat, 17 | output reg o_wb_ack 18 | ); 19 | 20 | // Wishbone register addresses 21 | localparam 22 | wb_r_PWM_PRESCALER = 1'b0, 23 | wb_r_BGR_DATA = 1'b1, 24 | wb_r_MAX = 1'b1; 25 | 26 | // PWM prescaler register 27 | reg [31:0] pwm_prescaler; 28 | reg [31:0] pwm_downcounter; 29 | reg [7:0] pwm_compare; 30 | 31 | // BGR output compare registers 32 | reg [7:0] ocr_b; 33 | reg [7:0] ocr_g; 34 | reg [7:0] ocr_r; 35 | 36 | // PWM generation 37 | always @(posedge i_clk) begin 38 | // Prescaled counter for the pwm compare register 39 | if (pwm_downcounter > 0) begin 40 | pwm_downcounter <= pwm_downcounter - 1; 41 | end else begin 42 | pwm_downcounter <= pwm_prescaler; 43 | pwm_compare <= pwm_compare + 1; 44 | end 45 | 46 | // Update output with the result of the compare registers 47 | o_led_bgr <= { 48 | pwm_compare >= ocr_b, 49 | pwm_compare >= ocr_g, 50 | pwm_compare >= ocr_r 51 | }; 52 | end 53 | 54 | // Since the incoming wishbone address from the CPU increments by 4 bytes, we 55 | // need to right shift it by 2 to get our actual register index 56 | localparam reg_sel_bits = $clog2(wb_r_MAX + 1); 57 | wire [reg_sel_bits-1:0] register_index = i_wb_adr[reg_sel_bits+1:2]; 58 | 59 | always @(posedge i_clk) begin 60 | if (i_reset) begin 61 | o_wb_ack <= 0; 62 | pwm_prescaler <= 0; 63 | end else begin 64 | // Wishbone interface logic 65 | o_wb_ack <= 1'b0; 66 | if (i_wb_cyc && i_wb_stb && !o_wb_ack) begin 67 | o_wb_ack <= 1'b1; 68 | 69 | // Register read 70 | case (register_index) 71 | wb_r_PWM_PRESCALER: o_wb_dat <= pwm_prescaler; 72 | wb_r_BGR_DATA: o_wb_dat <= {8'd0, ocr_b, ocr_g, ocr_r}; 73 | endcase 74 | 75 | // Register write 76 | if (i_wb_we) begin 77 | case (register_index) 78 | wb_r_PWM_PRESCALER: pwm_prescaler <= i_wb_dat; 79 | wb_r_BGR_DATA: begin 80 | ocr_b <= i_wb_dat[23:16]; 81 | ocr_g <= i_wb_dat[15:8]; 82 | ocr_r <= i_wb_dat[7:0]; 83 | end 84 | endcase 85 | end 86 | end 87 | end 88 | end 89 | 90 | endmodule 91 | -------------------------------------------------------------------------------- /rtl/timer_wb.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module timer_wb( 4 | // RCC 5 | input wire i_clk, 6 | input wire i_reset, 7 | // Trigger out signal 8 | output wire o_timer_trigger, 9 | // Wishbone 10 | input wire [31:0] i_wb_adr, 11 | input wire [31:0] i_wb_dat, 12 | input wire [3:0] i_wb_sel, 13 | input wire i_wb_we, 14 | input wire i_wb_cyc, 15 | input wire i_wb_stb, 16 | output reg [31:0] o_wb_dat, 17 | output reg o_wb_ack 18 | ); 19 | 20 | // Default prescaler value. Reloaded on reset. 21 | parameter DEFAULT_PRESCALER = 32'hFFFF_FFFF; 22 | 23 | // Prescaler value. Reloaded onto the downcounter on update. 24 | reg [31:0] prescaler = DEFAULT_PRESCALER; 25 | 26 | // Downcounter. Trigger output is latched high when this hits zero. 27 | reg [31:0] downcounter = DEFAULT_PRESCALER; 28 | 29 | // Register the output trigger signal 30 | reg timer_trigger = 0; 31 | assign o_timer_trigger = timer_trigger; 32 | 33 | // Bit indices for the flags register 34 | localparam 35 | wb_r_FLAGS__TRIGGER = 0; 36 | 37 | // Flags register is just a collection of bits, in this case just the trigger 38 | wire [31:0] flags; 39 | assign flags[wb_r_FLAGS__TRIGGER] = timer_trigger; 40 | assign flags[31:1] = 0; 41 | 42 | // Wishbone register addresses 43 | // Each register is actually 32 bits wide 44 | localparam 45 | wb_r_PRESCALER = 1'b0, 46 | wb_r_FLAGS = 1'b1, 47 | wb_r_MAX = 1'b1; 48 | 49 | // Since the incoming wishbone address from the CPU increments by 4 bytes, we 50 | // need to right shift it by 2 to get our actual register index 51 | localparam reg_sel_bits = $clog2(wb_r_MAX + 1); 52 | wire [reg_sel_bits-1:0] register_index = i_wb_adr[reg_sel_bits+1:2]; 53 | 54 | always @(posedge i_clk) begin 55 | if (i_reset) begin 56 | o_wb_ack <= 0; 57 | prescaler <= DEFAULT_PRESCALER; 58 | downcounter <= DEFAULT_PRESCALER; 59 | timer_trigger <= 1'b0; 60 | end else begin 61 | // Handle the downcounter 62 | if (downcounter > 0) begin 63 | downcounter <= downcounter - 1; 64 | end else begin 65 | downcounter <= prescaler; 66 | timer_trigger <= 1'b1; 67 | end 68 | 69 | // Wishbone interface logic 70 | o_wb_ack <= 1'b0; 71 | if (i_wb_cyc && i_wb_stb && !o_wb_ack) begin 72 | o_wb_ack <= 1'b1; 73 | 74 | // Register read 75 | case (register_index) 76 | wb_r_PRESCALER: o_wb_dat <= prescaler; 77 | wb_r_FLAGS: o_wb_dat <= flags; 78 | endcase 79 | 80 | // Register write 81 | if (i_wb_we) begin 82 | case (register_index) 83 | wb_r_PRESCALER: begin 84 | // Load the new prescaler, also reset the downcounter 85 | prescaler <= i_wb_dat; 86 | downcounter <= i_wb_dat; 87 | end 88 | wb_r_FLAGS: begin 89 | // If the trigger bit is written, clear the trig state 90 | if (i_wb_dat[wb_r_FLAGS__TRIGGER]) 91 | timer_trigger <= 1'b0; 92 | end 93 | endcase 94 | end 95 | end 96 | end 97 | end 98 | 99 | endmodule 100 | -------------------------------------------------------------------------------- /rtl/top.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | `define CLOCK_HZ 42_000_000 4 | 5 | module top( 6 | // External oscillator 7 | input wire CLK_12MHZ, 8 | 9 | // Debug LED 10 | output wire [2:0] LED, 11 | 12 | // Uart bridge to MCU 13 | input wire MCU_UART_TX, 14 | output wire MCU_UART_RX 15 | ); 16 | 17 | `include "gen/wb_intercon.vh" 18 | 19 | // PLL 20 | wire wb_clk; 21 | ice_pll pll ( 22 | .clock_in(CLK_12MHZ), 23 | .clock_out(wb_clk), 24 | .locked() 25 | ); 26 | 27 | // Initial reset 28 | reg [7:0] reset_counter = 8'hFF; 29 | wire reset = ~(reset_counter == 0); 30 | wire wb_rst = reset; // Generated wishbone bus assumes this signal 31 | always @(posedge wb_clk) 32 | if (reset_counter > 0) 33 | reset_counter <= reset_counter - 1; 34 | 35 | // CPU RAM 36 | wb_ram #( 37 | .SIZE(512) // in 32-bit words, so 2KiB 38 | ) cpu0_ram ( 39 | .i_clk(wb_clk), 40 | .i_reset(reset), 41 | .i_wb_adr(wb_m2s_cpu0_ram_adr), 42 | .i_wb_dat(wb_m2s_cpu0_ram_dat), 43 | .i_wb_sel(wb_m2s_cpu0_ram_sel), 44 | .i_wb_we (wb_m2s_cpu0_ram_we), 45 | .i_wb_cyc(wb_m2s_cpu0_ram_cyc), 46 | .i_wb_stb(wb_m2s_cpu0_ram_stb), 47 | .o_wb_dat(wb_s2m_cpu0_ram_dat), 48 | .o_wb_ack(wb_s2m_cpu0_ram_ack) 49 | ); 50 | 51 | // CPU ROM. 52 | // We initialize this directly from the hex file with our firmware in it 53 | wb_ram #( 54 | .SIZE(1024), // 4KiB 55 | .INITIAL_HEX("ice40_soc_fw_hex") 56 | ) cpu0_rom ( 57 | .i_clk(wb_clk), 58 | .i_reset(reset), 59 | .i_wb_adr(wb_m2s_cpu0_rom_adr), 60 | .i_wb_dat(wb_m2s_cpu0_rom_dat), 61 | .i_wb_sel(wb_m2s_cpu0_rom_sel), 62 | .i_wb_we (wb_m2s_cpu0_rom_we), 63 | .i_wb_cyc(wb_m2s_cpu0_rom_cyc), 64 | .i_wb_stb(wb_m2s_cpu0_rom_stb), 65 | .o_wb_dat(wb_s2m_cpu0_rom_dat), 66 | .o_wb_ack(wb_s2m_cpu0_rom_ack) 67 | ); 68 | 69 | // Timer for generating the timer interrupt 70 | wire timer_interrupt; 71 | timer_wb #( 72 | .DEFAULT_PRESCALER(`CLOCK_HZ / 1000 - 1) 73 | ) timer0 ( 74 | .i_clk(wb_clk), 75 | .i_reset(reset), 76 | .o_timer_trigger(timer_interrupt), 77 | .i_wb_adr(wb_m2s_timer0_adr), 78 | .i_wb_dat(wb_m2s_timer0_dat), 79 | .i_wb_sel(wb_m2s_timer0_sel), 80 | .i_wb_we (wb_m2s_timer0_we), 81 | .i_wb_cyc(wb_m2s_timer0_cyc), 82 | .i_wb_stb(wb_m2s_timer0_stb), 83 | .o_wb_dat(wb_s2m_timer0_dat), 84 | .o_wb_ack(wb_s2m_timer0_ack) 85 | ); 86 | 87 | // Uart for console logging 88 | uart_wb #( 89 | .TX_BUFSIZE(1024), 90 | .RX_BUFSIZE(16), 91 | .DATA_WIDTH(8) 92 | ) uart0 ( 93 | .i_clk(wb_clk), 94 | .i_reset(reset), 95 | .i_uart_rx(MCU_UART_TX), 96 | .o_uart_tx(MCU_UART_RX), 97 | .i_wb_adr(wb_m2s_uart0_adr), 98 | .i_wb_dat(wb_m2s_uart0_dat), 99 | .i_wb_sel(wb_m2s_uart0_sel), 100 | .i_wb_we (wb_m2s_uart0_we), 101 | .i_wb_cyc(wb_m2s_uart0_cyc), 102 | .i_wb_stb(wb_m2s_uart0_stb), 103 | .o_wb_dat(wb_s2m_uart0_dat), 104 | .o_wb_ack(wb_s2m_uart0_ack) 105 | ); 106 | 107 | // RGB LED 108 | 109 | rgb_led_wb led0 ( 110 | .i_clk(wb_clk), 111 | .i_reset(reset), 112 | .o_led_bgr(LED), 113 | .i_wb_adr(wb_m2s_led0_adr), 114 | .i_wb_dat(wb_m2s_led0_dat), 115 | .i_wb_sel(wb_m2s_led0_sel), 116 | .i_wb_we (wb_m2s_led0_we), 117 | .i_wb_cyc(wb_m2s_led0_cyc), 118 | .i_wb_stb(wb_m2s_led0_stb), 119 | .o_wb_dat(wb_s2m_led0_dat), 120 | .o_wb_ack(wb_s2m_led0_ack) 121 | ); 122 | 123 | 124 | // CPU 125 | VexRiscv cpu0 ( 126 | // RCC 127 | .clk(wb_clk), 128 | .reset(reset), 129 | 130 | // PC value after reset 131 | // Here hardcoded to our ROM address base 132 | .externalResetVector(32'h2000_0000), 133 | 134 | // Interrupt sources 135 | .timerInterrupt(timer_interrupt), 136 | .externalInterrupt(1'b0), 137 | .softwareInterrupt(1'b0), 138 | 139 | // Instruction bus 140 | .iBusWishbone_CYC(wb_m2s_cpu0_ibus_cyc), 141 | .iBusWishbone_STB(wb_m2s_cpu0_ibus_stb), 142 | .iBusWishbone_ACK(wb_s2m_cpu0_ibus_ack), 143 | .iBusWishbone_WE(wb_m2s_cpu0_ibus_we), 144 | .iBusWishbone_ADR(wb_m2s_cpu0_ibus_adr[31:2]), // Low 2 bits are always zero 145 | .iBusWishbone_DAT_MISO(wb_s2m_cpu0_ibus_dat), 146 | .iBusWishbone_DAT_MOSI(wb_m2s_cpu0_ibus_dat), 147 | .iBusWishbone_SEL(wb_m2s_cpu0_ibus_sel), 148 | .iBusWishbone_ERR(wb_s2m_cpu0_ibus_err), 149 | .iBusWishbone_BTE(wb_m2s_cpu0_ibus_bte), 150 | .iBusWishbone_CTI(wb_m2s_cpu0_ibus_cti), 151 | 152 | // Data bus 153 | .dBusWishbone_CYC(wb_m2s_cpu0_dbus_cyc), 154 | .dBusWishbone_STB(wb_m2s_cpu0_dbus_stb), 155 | .dBusWishbone_ACK(wb_s2m_cpu0_dbus_ack), 156 | .dBusWishbone_WE(wb_m2s_cpu0_dbus_we), 157 | .dBusWishbone_ADR(wb_m2s_cpu0_dbus_adr[31:2]), 158 | .dBusWishbone_DAT_MISO(wb_s2m_cpu0_dbus_dat), 159 | .dBusWishbone_DAT_MOSI(wb_m2s_cpu0_dbus_dat), 160 | .dBusWishbone_SEL(wb_m2s_cpu0_dbus_sel), 161 | .dBusWishbone_ERR(wb_s2m_cpu0_dbus_err), 162 | .dBusWishbone_BTE(wb_m2s_cpu0_dbus_bte), 163 | .dBusWishbone_CTI(wb_m2s_cpu0_dbus_cti) 164 | ); 165 | 166 | endmodule 167 | -------------------------------------------------------------------------------- /rtl/uart_rx.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module uart_rx ( 4 | // Global clock 5 | input wire i_clk, 6 | input wire i_reset, 7 | // Physical UART input 8 | input wire i_uart_rx, 9 | // Output data 10 | output wire [DATA_WIDTH-1:0] o_data, 11 | // Output data valid strobe 12 | output wire o_data_stb, 13 | // Prescaler reload value 14 | // Must be externally registered 15 | input wire [15:0] i_baudrate_prescaler 16 | ); 17 | 18 | parameter DATA_WIDTH = 16; 19 | 20 | // Delay latches for metastability 21 | reg [2:0] r_uart_rx; 22 | wire uart_rx = r_uart_rx[2]; 23 | always @(posedge i_clk) begin 24 | r_uart_rx <= {r_uart_rx[1], r_uart_rx[0], i_uart_rx}; 25 | end 26 | 27 | // Data register 28 | reg [DATA_WIDTH-1:0] r_data = 0; 29 | assign o_data = r_data; 30 | reg r_data_valid = 0; 31 | assign o_data_stb = r_data_valid; 32 | 33 | // Received bits counter 34 | reg [$clog2(DATA_WIDTH):0] r_rx_bit_count = 0; 35 | 36 | // State 37 | reg [1:0] state = s_IDLE; 38 | localparam 39 | s_IDLE = 0, 40 | s_RECEIVE = 1, 41 | s_STOP = 2; 42 | 43 | // Baudrate clock generator 44 | // When the logic is in s_IDLE, the prescaler counter is loaded with 45 | // 1.5 times the normal period so that we sample nicely in the middle 46 | // of each bit 47 | reg [15:0] r_prescaler_counter= 0; 48 | reg r_baud_clock; 49 | always @(posedge i_clk) begin 50 | if (i_reset) begin 51 | r_prescaler_counter <= i_baudrate_prescaler; 52 | r_baud_clock <= 0; 53 | end else begin 54 | if (state == s_IDLE) begin 55 | // Keep prescaler loaded with 1.5x the normal value for offset 56 | r_prescaler_counter <= (i_baudrate_prescaler + (i_baudrate_prescaler >> 1)); 57 | r_baud_clock <= 0; 58 | end else begin 59 | if (r_prescaler_counter > 0) begin 60 | r_prescaler_counter <= r_prescaler_counter - 1; 61 | r_baud_clock <= 0; 62 | end else begin 63 | r_baud_clock <= ~r_baud_clock; 64 | r_prescaler_counter <= i_baudrate_prescaler; 65 | r_baud_clock <= 1; 66 | end 67 | end 68 | end 69 | end 70 | 71 | // Receive logic 72 | always @(posedge i_clk) begin 73 | if (i_reset) begin 74 | r_data <= 0; 75 | r_data_valid <= 0; 76 | r_rx_bit_count <= 0; 77 | state <= s_IDLE; 78 | end else begin 79 | case (state) 80 | 81 | s_IDLE: begin 82 | // Wait for the uart data to go low for the start bit 83 | if (uart_rx == 1'b0) begin 84 | r_rx_bit_count <= 0; 85 | state <= s_RECEIVE; 86 | end 87 | // Clear DV line 88 | r_data_valid <= 1'b0; 89 | end 90 | 91 | s_RECEIVE: begin 92 | // If the rx baud clock pulsed, sample the RX data 93 | if (r_baud_clock) begin 94 | // Sample the input data 95 | r_data <= {uart_rx, r_data[DATA_WIDTH-1:1]}; 96 | // Increment the rx bit count 97 | r_rx_bit_count <= r_rx_bit_count + 1; 98 | end 99 | 100 | // If we have received all the bits, strobe output and idle to 101 | // handle the stop bit 102 | if (r_rx_bit_count == DATA_WIDTH[$clog2(DATA_WIDTH):0]) begin 103 | r_data_valid <= 1'b1; 104 | state <= s_STOP; 105 | end 106 | end 107 | 108 | s_STOP: begin 109 | // Simply wait for one more clock of the baud clock so that 110 | // we do not accidentally re-trigger as the transmitter 111 | // goes to the stop bit 112 | if (r_baud_clock) begin 113 | state <= s_IDLE; 114 | end 115 | r_data_valid <= 1'b0; 116 | end 117 | 118 | 119 | default: begin 120 | state <= s_IDLE; 121 | end 122 | 123 | endcase 124 | end 125 | end 126 | 127 | endmodule 128 | -------------------------------------------------------------------------------- /rtl/uart_tx.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module uart_tx ( 4 | // Global clock 5 | input wire i_clk, 6 | input wire i_reset, 7 | // Physical UART output 8 | output wire o_uart_tx, 9 | // Data word to send 10 | input wire [DATA_WIDTH-1:0] i_data, 11 | // Input data valid 12 | input wire i_data_stb, 13 | // Status 14 | output wire o_busy, 15 | // Prescaler reload value 16 | // Must be externally registered 17 | input wire [15:0] i_baudrate_prescaler 18 | ); 19 | 20 | parameter DATA_WIDTH = 16; 21 | 22 | // Register some of our IO wires 23 | reg o_uart_tx_reg = 1; 24 | reg o_busy_reg = 0; 25 | assign o_uart_tx = o_uart_tx_reg; 26 | assign o_busy = o_busy_reg; 27 | 28 | // Internal data register 29 | reg [DATA_WIDTH:0] data_reg = 0; 30 | // Prescale register 31 | reg [15:0] prescale_reg = 0; 32 | // How many bits remain to be sent 33 | reg [$clog2(DATA_WIDTH+1):0] bit_cnt = 0; 34 | 35 | always @(posedge i_clk) begin 36 | if (i_reset) begin 37 | o_uart_tx_reg <= 1; 38 | prescale_reg <= 0; 39 | bit_cnt <= 0; 40 | o_busy_reg <= 0; 41 | end else begin 42 | // If we are prescaling, continue counting down to zero 43 | if (prescale_reg > 0) begin 44 | prescale_reg <= prescale_reg - 1; 45 | end else if (bit_cnt == 0) begin 46 | // If we are idle and the input data line is valid, load 47 | // the input data 48 | if (i_data_stb) begin 49 | // Reset prescaler register 50 | prescale_reg <= i_baudrate_prescaler; 51 | // Load the bit count with the data width + stop bit 52 | bit_cnt <= DATA_WIDTH+1; 53 | // Data register is stop bit + input data 54 | data_reg <= {1'b1, i_data}; 55 | // Start sending the start bit 56 | o_uart_tx_reg <= 0; 57 | // Transmit in progress 58 | o_busy_reg <= 1; 59 | end else begin 60 | // If the current bit count is zero (no data remaining to be 61 | // sent) go back to being ready 62 | o_busy_reg <= 0; 63 | end 64 | end else begin 65 | // Not in reset and bit count is non-zero 66 | if (bit_cnt > 1) begin 67 | // Many bits remain. 68 | // Decrement the number of bits to send 69 | bit_cnt <= bit_cnt - 1; 70 | // Reset our prescaler register 71 | prescale_reg <= i_baudrate_prescaler; 72 | // Right shift the data register, loading the lsb into our 73 | // transmit data register 74 | {data_reg, o_uart_tx_reg} <= {1'b0, data_reg}; 75 | end else if (bit_cnt == 1) begin 76 | // This is the final (stop) bit. 77 | bit_cnt <= bit_cnt - 1; 78 | prescale_reg <= i_baudrate_prescaler; 79 | o_uart_tx_reg <= 1; 80 | end 81 | end 82 | end 83 | end 84 | 85 | endmodule 86 | -------------------------------------------------------------------------------- /rtl/uart_wb.v: -------------------------------------------------------------------------------- 1 | module uart_wb( 2 | // RCC 3 | input wire i_clk, 4 | input wire i_reset, 5 | // Physical signals 6 | input wire i_uart_rx, 7 | output wire o_uart_tx, 8 | // Wishbone 9 | input wire [31:0] i_wb_adr, 10 | input wire [31:0] i_wb_dat, 11 | input wire [3:0] i_wb_sel, 12 | input wire i_wb_we, 13 | input wire i_wb_cyc, 14 | input wire i_wb_stb, 15 | output reg [31:0] o_wb_dat, 16 | output reg o_wb_ack 17 | ); 18 | 19 | parameter TX_BUFSIZE = 8; 20 | parameter RX_BUFSIZE = 8; 21 | parameter DATA_WIDTH = 16; 22 | 23 | reg [15:0] baudrate_prescaler = 0; 24 | 25 | // Transmit block 26 | wire [DATA_WIDTH-1:0] rx_data; 27 | wire rx_stb; 28 | uart_rx #( 29 | .DATA_WIDTH(DATA_WIDTH) 30 | ) rx0 ( 31 | .i_clk(i_clk), 32 | .i_reset(i_reset), 33 | .i_uart_rx(i_uart_rx), 34 | .o_data(rx_data), 35 | .o_data_stb(rx_stb), 36 | .i_baudrate_prescaler(baudrate_prescaler) 37 | ); 38 | 39 | // Receive block 40 | wire [DATA_WIDTH-1:0] tx_data; 41 | wire tx_stb; 42 | wire tx_busy; 43 | uart_tx #( 44 | .DATA_WIDTH(DATA_WIDTH) 45 | ) tx0 ( 46 | .i_clk(i_clk), 47 | .i_reset(i_reset), 48 | .o_uart_tx(o_uart_tx), 49 | .i_data(tx_data), 50 | .i_data_stb(tx_stb), 51 | .o_busy(tx_busy), 52 | .i_baudrate_prescaler(baudrate_prescaler) 53 | ); 54 | 55 | // RX FIFO 56 | wire [DATA_WIDTH-1:0] rx_fifo_r_data; 57 | reg rx_fifo_r_stb; 58 | wire rx_fifo_full; 59 | wire rx_fifo_empty; 60 | wire [$clog2(RX_BUFSIZE)-1:0] rx_fifo_item_count; 61 | wire [$clog2(RX_BUFSIZE)-1:0] rx_fifo_free_size; 62 | fifo #( 63 | .DATA_WIDTH(DATA_WIDTH), 64 | .MAX_ENTRIES(RX_BUFSIZE) 65 | ) rx0_fifo ( 66 | .i_reset(i_reset), 67 | .i_clk(i_clk), 68 | // Write interface: direct from UART 69 | .i_w_data(rx_data), 70 | .i_w_data_stb(rx_stb), 71 | // Read interface: to wishbone 72 | .o_r_data(rx_fifo_r_data), 73 | .i_r_data_stb(rx_fifo_r_stb), 74 | .o_full(rx_fifo_full), 75 | .o_empty(rx_fifo_empty), 76 | .o_item_count(rx_fifo_item_count), 77 | .o_free_size(rx_fifo_free_size) 78 | ); 79 | 80 | // TX FIFO 81 | reg [DATA_WIDTH-1:0] tx_fifo_w_data; 82 | reg tx_fifo_w_stb; 83 | wire tx_fifo_full; 84 | wire tx_fifo_empty; 85 | wire [$clog2(TX_BUFSIZE)-1:0] tx_fifo_item_count; 86 | wire [$clog2(TX_BUFSIZE)-1:0] tx_fifo_free_size; 87 | fifo #( 88 | .DATA_WIDTH(DATA_WIDTH), 89 | .MAX_ENTRIES(TX_BUFSIZE) 90 | ) tx0_fifo ( 91 | .i_reset(i_reset), 92 | .i_clk(i_clk), 93 | // Write interface: from wishbone 94 | .i_w_data(tx_fifo_w_data), 95 | .i_w_data_stb(tx_fifo_w_stb), 96 | // Read interface: to uart 97 | .o_r_data(tx_data), 98 | .i_r_data_stb(tx_stb), 99 | .o_full(tx_fifo_full), 100 | .o_empty(tx_fifo_empty), 101 | .o_item_count(tx_fifo_item_count), 102 | .o_free_size(tx_fifo_free_size) 103 | ); 104 | 105 | // Strobe the uart TX whenever the uart isn't busy and the fifo isn't empty 106 | assign tx_stb = !tx_busy && !tx_fifo_empty; 107 | 108 | // Wishbone logic 109 | localparam 110 | // General 111 | wb_r_conf_prescaler = 6'h00, 112 | // TX 113 | wb_r_tx_flags = 6'h10, 114 | wb_r_tx_item_count = 6'h11, 115 | wb_r_tx_free_size = 6'h12, 116 | wb_r_tx_write = 6'h13, 117 | // RX 118 | wb_r_rx_flags = 6'h20, 119 | wb_r_rx_item_count = 6'h21, 120 | wb_r_rx_free_size = 6'h22, 121 | wb_r_rx_read = 6'h23, 122 | // 123 | wb_r_max = 6'h23; 124 | 125 | 126 | wire [31:0] tx_flags = {30'd0, tx_fifo_full, tx_fifo_empty}; 127 | wire [31:0] rx_flags = {30'd0, rx_fifo_full, rx_fifo_empty}; 128 | 129 | `define EXTEND(value) {{32-$bits(value){1'b0}}, value} 130 | 131 | // Adjust register offset for 32 bit words 132 | localparam addr_bits = $clog2(wb_r_max+1); 133 | wire [addr_bits-1:0] register_index = i_wb_adr[addr_bits+1:2]; 134 | 135 | always @(posedge i_clk) begin 136 | if (i_reset) begin 137 | o_wb_ack <= 1'b0; 138 | end else begin 139 | 140 | // Clear strobes 141 | o_wb_ack <= 1'b0; 142 | rx_fifo_r_stb <= 1'b0; 143 | tx_fifo_w_stb <= 1'b0; 144 | 145 | if (i_wb_cyc && i_wb_stb && !o_wb_ack) begin 146 | // Reads 147 | case (register_index) 148 | 149 | wb_r_conf_prescaler: o_wb_dat <= `EXTEND(baudrate_prescaler); 150 | 151 | wb_r_tx_flags: o_wb_dat <= tx_flags; 152 | wb_r_tx_item_count: o_wb_dat <= `EXTEND(tx_fifo_item_count); 153 | wb_r_tx_free_size: o_wb_dat <= `EXTEND(tx_fifo_free_size); 154 | 155 | wb_r_rx_read: begin 156 | o_wb_dat <= `EXTEND(rx_fifo_r_data); 157 | rx_fifo_r_stb <= 1'b1; 158 | end 159 | wb_r_rx_flags: o_wb_dat <= rx_flags; 160 | wb_r_rx_item_count: o_wb_dat <= `EXTEND(rx_fifo_item_count); 161 | wb_r_rx_free_size: o_wb_dat <= `EXTEND(rx_fifo_free_size); 162 | 163 | default: /* Bad register */ o_wb_dat <= 32'hDEADBEEF; 164 | endcase 165 | 166 | // Writes 167 | if (i_wb_we) begin 168 | case (register_index) 169 | 170 | wb_r_conf_prescaler: baudrate_prescaler <= i_wb_dat[$bits(baudrate_prescaler)-1:0]; 171 | 172 | wb_r_tx_write: begin 173 | tx_fifo_w_data <= i_wb_dat[DATA_WIDTH-1:0]; 174 | tx_fifo_w_stb <= 1'b1; 175 | end 176 | 177 | default: /* Bad register */ ; 178 | endcase 179 | end 180 | 181 | o_wb_ack <= 1'b1; 182 | end 183 | 184 | end 185 | end 186 | 187 | endmodule 188 | -------------------------------------------------------------------------------- /rtl/wb_ram.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | 3 | module wb_ram( 4 | // RCC 5 | input wire i_clk, 6 | input wire i_reset, 7 | // Wishbone 8 | input wire [31:0] i_wb_adr, 9 | input wire [31:0] i_wb_dat, 10 | input wire [3:0] i_wb_sel, 11 | input wire i_wb_we, 12 | input wire i_wb_cyc, 13 | input wire i_wb_stb, 14 | output reg [31:0] o_wb_dat, 15 | output reg o_wb_ack 16 | ); 17 | 18 | // Number of 32-bit words to store 19 | parameter SIZE = 512; 20 | 21 | // Data storage 22 | reg [31:0] data [SIZE]; 23 | 24 | // If we have been given an initial file parameter, load that 25 | parameter INITIAL_HEX = ""; 26 | initial begin 27 | /* verilator lint_off WIDTH */ 28 | if (INITIAL_HEX != "") 29 | $readmemh(INITIAL_HEX, data); 30 | /* verilator lint_on WIDTH */ 31 | end 32 | 33 | // Each data entry is 32 bits wide, so right shift the input address 34 | localparam addr_width = $clog2(SIZE); 35 | wire [addr_width-1:0] data_addr = i_wb_adr[addr_width+1:2]; 36 | 37 | integer i; 38 | always @(posedge i_clk) begin 39 | if (i_reset) begin 40 | o_wb_ack <= 1'b0; 41 | end else begin 42 | o_wb_ack <= 1'b0; 43 | 44 | if (i_wb_cyc & i_wb_stb & ~o_wb_ack) begin 45 | // Always ack 46 | o_wb_ack <= 1'b1; 47 | 48 | // Reads 49 | o_wb_dat <= data[data_addr]; 50 | 51 | // Writes 52 | if (i_wb_we) begin 53 | for (i = 0; i < 4; i++) begin 54 | if (i_wb_sel[i]) 55 | data[data_addr][i*8 +: 8] <= i_wb_dat[i*8 +: 8]; 56 | end 57 | end 58 | end 59 | end 60 | end 61 | 62 | endmodule 63 | -------------------------------------------------------------------------------- /scripts/makehex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from sys import argv 3 | 4 | binfile = argv[1] 5 | 6 | with open(binfile, "rb") as f: 7 | bindata = bytearray(f.read()) 8 | 9 | # Pad data if necessary 10 | pad_count = 4 - (len(bindata) % 4) 11 | for _ in range(pad_count): 12 | bindata.append(0) 13 | 14 | assert len(bindata) % 4 == 0 15 | 16 | for i in range(len(bindata) // 4): 17 | w = bindata[4*i : 4*i+4] 18 | print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0])) 19 | -------------------------------------------------------------------------------- /sim.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.98 (w)1999-2019 BSI 3 | [*] Sun Aug 23 15:38:18 2020 4 | [*] 5 | [dumpfile] "/home/ross/NetProgramming/Github/fpga-swe-3-soc/build/sim_out.vcd" 6 | [dumpfile_mtime] "Sun Aug 23 00:33:13 2020" 7 | [dumpfile_size] 173603443 8 | [savefile] "/home/ross/NetProgramming/Github/fpga-swe-3-soc/sim.gtkw" 9 | [timestart] 66650 10 | [size] 2646 1594 11 | [pos] -1 -1 12 | *-11.000000 69130 6487140 6465940 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 13 | [treeopen] TOP. 14 | [treeopen] TOP.top. 15 | [sst_width] 502 16 | [signals_width] 722 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 594 19 | @28 20 | TOP.top.wb_clk 21 | TOP.top.reset 22 | @200 23 | - 24 | @22 25 | TOP.top.cpu0.execute_PC[31:0] 26 | TOP.top.cpu0.CsrPlugin_mcycle[63:0] 27 | @28 28 | TOP.top.cpu0.CsrPlugin_exception 29 | @22 30 | TOP.top.cpu0.CsrPlugin_mcause_exceptionCode[3:0] 31 | TOP.top.cpu0.CsrPlugin_mtvec_base[29:0] 32 | @28 33 | TOP.top.cpu0.CsrPlugin_mtvec_mode[1:0] 34 | TOP.top.cpu0.CsrPlugin_mie_MTIE 35 | TOP.top.cpu0.CsrPlugin_mstatus_MIE 36 | @200 37 | - 38 | @22 39 | TOP.top.wb_m2s_cpu0_ibus_adr[31:0] 40 | TOP.top.wb_m2s_cpu0_ibus_dat[31:0] 41 | TOP.top.wb_m2s_cpu0_ibus_sel[3:0] 42 | @28 43 | TOP.top.wb_m2s_cpu0_ibus_we 44 | TOP.top.wb_m2s_cpu0_ibus_cyc 45 | TOP.top.wb_m2s_cpu0_ibus_stb 46 | @22 47 | TOP.top.wb_s2m_cpu0_ibus_dat[31:0] 48 | @28 49 | TOP.top.wb_s2m_cpu0_ibus_ack 50 | @200 51 | - 52 | @22 53 | TOP.top.wb_m2s_cpu0_dbus_adr[31:0] 54 | TOP.top.wb_m2s_cpu0_dbus_dat[31:0] 55 | TOP.top.wb_m2s_cpu0_dbus_sel[3:0] 56 | @28 57 | TOP.top.wb_m2s_cpu0_dbus_we 58 | TOP.top.wb_m2s_cpu0_dbus_cyc 59 | TOP.top.wb_m2s_cpu0_dbus_stb 60 | @22 61 | TOP.top.wb_s2m_cpu0_dbus_dat[31:0] 62 | @28 63 | TOP.top.wb_s2m_cpu0_dbus_ack 64 | @200 65 | - 66 | @22 67 | TOP.top.wb_m2s_timer0_adr[31:0] 68 | TOP.top.wb_m2s_timer0_dat[31:0] 69 | TOP.top.wb_m2s_timer0_sel[3:0] 70 | @28 71 | TOP.top.wb_m2s_timer0_we 72 | TOP.top.wb_m2s_timer0_cyc 73 | TOP.top.wb_m2s_timer0_stb 74 | @22 75 | TOP.top.wb_s2m_timer0_dat[31:0] 76 | @28 77 | TOP.top.wb_s2m_timer0_ack 78 | TOP.top.timer_interrupt 79 | @200 80 | - 81 | @22 82 | TOP.top.timer0.downcounter[31:0] 83 | TOP.top.timer0.prescaler[31:0] 84 | @200 85 | - 86 | @22 87 | TOP.top.wb_m2s_uart0_adr[31:0] 88 | TOP.top.wb_m2s_uart0_dat[31:0] 89 | TOP.top.wb_m2s_uart0_sel[3:0] 90 | @28 91 | TOP.top.wb_m2s_uart0_we 92 | TOP.top.wb_m2s_uart0_cyc 93 | TOP.top.wb_m2s_uart0_stb 94 | @22 95 | TOP.top.wb_s2m_uart0_dat[31:0] 96 | @28 97 | TOP.top.wb_s2m_uart0_ack 98 | TOP.top.MCU_UART_TX 99 | TOP.top.MCU_UART_RX 100 | @200 101 | - 102 | @22 103 | TOP.top.wb_m2s_led0_adr[31:0] 104 | TOP.top.wb_m2s_led0_dat[31:0] 105 | TOP.top.wb_m2s_led0_sel[3:0] 106 | @28 107 | TOP.top.wb_m2s_led0_we 108 | TOP.top.wb_m2s_led0_cyc 109 | TOP.top.wb_m2s_led0_stb 110 | @22 111 | TOP.top.wb_s2m_led0_dat[31:0] 112 | @28 113 | TOP.top.wb_s2m_led0_ack 114 | @200 115 | - 116 | @22 117 | TOP.top.led0.ocr_b[7:0] 118 | TOP.top.led0.ocr_g[7:0] 119 | TOP.top.led0.ocr_r[7:0] 120 | @200 121 | - 122 | @22 123 | TOP.top.cpu0.execute_PC[31:0] 124 | TOP.top.wb_m2s_cpu0_dbus_adr[31:0] 125 | @28 126 | TOP.top.wb_m2s_cpu0_dbus_we 127 | TOP.top.wb_s2m_cpu0_dbus_ack 128 | TOP.top.cpu0.IBusCachedPlugin_jump_pcLoad_valid 129 | @22 130 | TOP.top.cpu0.IBusCachedPlugin_jump_pcLoad_payload[31:0] 131 | TOP.top.cpu0.RegFilePlugin_regFile(0)[31:0] 132 | TOP.top.cpu0.RegFilePlugin_regFile(1)[31:0] 133 | TOP.top.cpu0.RegFilePlugin_regFile(2)[31:0] 134 | TOP.top.cpu0.RegFilePlugin_regFile(3)[31:0] 135 | TOP.top.cpu0.RegFilePlugin_regFile(4)[31:0] 136 | TOP.top.cpu0.RegFilePlugin_regFile(5)[31:0] 137 | TOP.top.cpu0.RegFilePlugin_regFile(6)[31:0] 138 | TOP.top.cpu0.RegFilePlugin_regFile(7)[31:0] 139 | TOP.top.cpu0.RegFilePlugin_regFile(8)[31:0] 140 | TOP.top.cpu0.RegFilePlugin_regFile(9)[31:0] 141 | TOP.top.cpu0.RegFilePlugin_regFile(10)[31:0] 142 | TOP.top.cpu0.RegFilePlugin_regFile(11)[31:0] 143 | TOP.top.cpu0.RegFilePlugin_regFile(12)[31:0] 144 | TOP.top.cpu0.RegFilePlugin_regFile(13)[31:0] 145 | TOP.top.cpu0.RegFilePlugin_regFile(14)[31:0] 146 | TOP.top.cpu0.RegFilePlugin_regFile(15)[31:0] 147 | TOP.top.cpu0.RegFilePlugin_regFile(16)[31:0] 148 | TOP.top.cpu0.RegFilePlugin_regFile(17)[31:0] 149 | TOP.top.cpu0.RegFilePlugin_regFile(18)[31:0] 150 | TOP.top.cpu0.RegFilePlugin_regFile(19)[31:0] 151 | TOP.top.cpu0.RegFilePlugin_regFile(20)[31:0] 152 | TOP.top.cpu0.RegFilePlugin_regFile(21)[31:0] 153 | TOP.top.cpu0.RegFilePlugin_regFile(22)[31:0] 154 | TOP.top.cpu0.RegFilePlugin_regFile(23)[31:0] 155 | TOP.top.cpu0.RegFilePlugin_regFile(24)[31:0] 156 | TOP.top.cpu0.RegFilePlugin_regFile(25)[31:0] 157 | TOP.top.cpu0.RegFilePlugin_regFile(26)[31:0] 158 | TOP.top.cpu0.RegFilePlugin_regFile(27)[31:0] 159 | TOP.top.cpu0.RegFilePlugin_regFile(28)[31:0] 160 | TOP.top.cpu0.RegFilePlugin_regFile(29)[31:0] 161 | TOP.top.cpu0.RegFilePlugin_regFile(30)[31:0] 162 | TOP.top.cpu0.RegFilePlugin_regFile(31)[31:0] 163 | [pattern_trace] 1 164 | [pattern_trace] 0 165 | -------------------------------------------------------------------------------- /sim/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | int main() { 12 | Verilated::traceEverOn(true); 13 | Vtop *top = new Vtop; 14 | VerilatedVcdC *trace = new VerilatedVcdC(); 15 | top->trace(trace, 99); 16 | trace->open("sim_out.vcd"); 17 | 18 | const float wb_clk_hz = 12'000'000; 19 | const float nanoseconds_per_wb_clk = 1'000'000'000 / wb_clk_hz; 20 | float ns_since_last_wb_clk_tick = 0; 21 | 22 | for (int i = 0; i < 8'000'000; i++) { 23 | // Update the clock 24 | if (ns_since_last_wb_clk_tick++ > nanoseconds_per_wb_clk / 2) { 25 | top->CLK_12MHZ = top->CLK_12MHZ ? 0 : 1; 26 | ns_since_last_wb_clk_tick -= nanoseconds_per_wb_clk; 27 | } 28 | 29 | top->eval(); 30 | trace->dump(i); 31 | } 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /test/fifo_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "test_utils.hpp" 6 | 7 | // Value of MAX_ENTRIES parameter minus one 8 | static const int FIFO_MAX_ITEM_COUNT = 7; 9 | 10 | TEST_CASE("FIFO R+W (not-full case)", "[FIFO]") { 11 | TestData td("fifo_rw_not_full.vcd"); 12 | td.reset(); 13 | 14 | // FIFO should begin life empty 15 | REQUIRE(td.module->o_empty == 1); 16 | REQUIRE(td.module->o_full == 0); 17 | REQUIRE(td.module->o_item_count == 0); 18 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 19 | 20 | // Write a few data to the fifo 21 | uint16_t write_data[3] = {0xC0DE, 0xDEAD, 0xBEEF}; 22 | for (int i = 0; i < sizeof(write_data) / sizeof(uint16_t); i++) { 23 | // Write the item to the fifo 24 | td.module->i_clk = 1; 25 | td.module->i_w_data = write_data[i]; 26 | td.module->i_w_data_stb = 1; 27 | td.eval(); 28 | 29 | // If this was the first datum, the output data on the read interface should 30 | // now be valid 31 | if (i == 0) { 32 | REQUIRE(td.module->o_r_data == write_data[0]); 33 | } 34 | 35 | // Assert that the fifo values update 36 | REQUIRE(td.module->o_empty == 0); 37 | REQUIRE(td.module->o_full == 0); 38 | REQUIRE(td.module->o_item_count == i + 1); 39 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT - (i + 1)); 40 | td.module->i_clk = 0; 41 | td.eval(); 42 | } 43 | 44 | // Current output on the read interface should be the first data we wrote 45 | REQUIRE(td.module->o_r_data == write_data[0]); 46 | 47 | // Perform a read+write on the same cycle 48 | td.module->i_clk = 1; 49 | td.module->i_w_data = 0xFEED; 50 | td.module->i_w_data_stb = 1; 51 | td.module->i_r_data_stb = 1; 52 | td.eval(); 53 | 54 | // Size should still be 3 55 | REQUIRE(td.module->o_empty == 0); 56 | REQUIRE(td.module->o_full == 0); 57 | REQUIRE(td.module->o_item_count == 3); 58 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT - 3); 59 | 60 | // There is a one cycle delay on the read, so the next posedge should have the 61 | // new data on the read interface 62 | td.module->i_clk = 0; 63 | td.module->i_w_data_stb = 0; 64 | td.module->i_r_data_stb = 0; 65 | td.eval(); 66 | td.module->i_clk = 1; 67 | td.eval(); 68 | REQUIRE(td.module->o_r_data == write_data[1]); 69 | } 70 | 71 | TEST_CASE("FIFO R+W (full case)", "[FIFO]") { 72 | TestData td("fifo_rw_full.vcd"); 73 | td.reset(); 74 | 75 | // FIFO should begin life empty 76 | REQUIRE(td.module->o_empty == 1); 77 | REQUIRE(td.module->o_full == 0); 78 | REQUIRE(td.module->o_item_count == 0); 79 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 80 | 81 | // Write until FIFO full 82 | uint16_t w_data[FIFO_MAX_ITEM_COUNT]; 83 | int i = 0; 84 | while (!td.module->o_full) { 85 | // Write a random 16-bit word to the fifo 86 | td.module->i_clk = 1; 87 | td.module->i_w_data = rand() & 0xFFFF; 88 | w_data[i] = td.module->i_w_data; 89 | CAPTURE(td.module->i_w_data); 90 | td.module->i_w_data_stb = 1; 91 | td.eval(); 92 | 93 | // Assert the size has increased 94 | i++; 95 | REQUIRE(td.module->o_empty == 0); 96 | REQUIRE(td.module->o_item_count == i); 97 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT - i); 98 | td.module->i_clk = 0; 99 | td.eval(); 100 | } 101 | 102 | // The FIFO should now be totally full 103 | REQUIRE(td.module->o_empty == 0); 104 | REQUIRE(td.module->o_full == 1); 105 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT); 106 | REQUIRE(td.module->o_free_size == 0); 107 | 108 | // Perform a read and write on the same cycle 109 | td.module->i_clk = 1; 110 | td.module->i_w_data = rand() & 0xFFFF; 111 | td.module->i_w_data_stb = 1; 112 | td.module->i_r_data_stb = 1; 113 | td.eval(); 114 | 115 | // The fifo should still be full 116 | REQUIRE(td.module->o_empty == 0); 117 | REQUIRE(td.module->o_full == 1); 118 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT); 119 | REQUIRE(td.module->o_free_size == 0); 120 | 121 | // Output data should be 0th written item until the next posedge 122 | REQUIRE(td.module->o_r_data == w_data[0]); 123 | td.module->i_clk = 0; 124 | td.eval(); 125 | td.module->i_clk = 1; 126 | td.eval(); 127 | REQUIRE(td.module->o_r_data == w_data[1]); 128 | } 129 | 130 | TEST_CASE("FIFO R (empty)", "[FIFO]") { 131 | TestData td("fifo_r_empty.vcd"); 132 | td.reset(); 133 | 134 | // Assert empty 135 | REQUIRE(td.module->o_empty == 1); 136 | REQUIRE(td.module->o_full == 0); 137 | REQUIRE(td.module->o_item_count == 0); 138 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 139 | 140 | // Try and read 141 | td.module->i_clk = 1; 142 | td.module->i_r_data_stb = 1; 143 | td.eval(); 144 | 145 | // Should still be empty 146 | REQUIRE(td.module->o_empty == 1); 147 | REQUIRE(td.module->o_full == 0); 148 | REQUIRE(td.module->o_item_count == 0); 149 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 150 | } 151 | 152 | TEST_CASE("FIFO R (full)", "[FIFO]") { 153 | TestData td("fifo_r_full.vcd"); 154 | td.reset(); 155 | 156 | // Assert empty 157 | REQUIRE(td.module->o_empty == 1); 158 | REQUIRE(td.module->o_full == 0); 159 | REQUIRE(td.module->o_item_count == 0); 160 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 161 | 162 | // Write until FIFO full 163 | uint16_t w_data[FIFO_MAX_ITEM_COUNT]; 164 | int i = 0; 165 | while (!td.module->o_full) { 166 | // Write a random 16-bit word to the fifo 167 | td.module->i_clk = 1; 168 | td.module->i_w_data = rand() & 0xFFFF; 169 | w_data[i] = td.module->i_w_data; 170 | CAPTURE(td.module->i_w_data); 171 | td.module->i_w_data_stb = 1; 172 | td.eval(); 173 | 174 | // Assert the size has increased 175 | i++; 176 | REQUIRE(td.module->o_empty == 0); 177 | REQUIRE(td.module->o_item_count == i); 178 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT - i); 179 | td.module->i_clk = 0; 180 | td.eval(); 181 | } 182 | 183 | // Assert full 184 | REQUIRE(td.module->o_empty == 0); 185 | REQUIRE(td.module->o_full == 1); 186 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT); 187 | REQUIRE(td.module->o_free_size == 0); 188 | 189 | // Read something 190 | td.module->i_clk = 1; 191 | td.module->i_w_data_stb = 0; 192 | td.module->i_r_data_stb = 1; 193 | td.eval(); 194 | 195 | // Should no longer be full 196 | REQUIRE(td.module->o_empty == 0); 197 | REQUIRE(td.module->o_full == 0); 198 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT - 1); 199 | REQUIRE(td.module->o_free_size == 1); 200 | 201 | // Output data should be 0th written item until the next posedge 202 | REQUIRE(td.module->o_r_data == w_data[0]); 203 | td.module->i_clk = 0; 204 | td.eval(); 205 | td.module->i_clk = 1; 206 | td.eval(); 207 | REQUIRE(td.module->o_r_data == w_data[1]); 208 | } 209 | 210 | TEST_CASE("FIFO W (full)", "[FIFO]") { 211 | TestData td("fifo_w_full.vcd"); 212 | td.reset(); 213 | 214 | // Assert empty 215 | REQUIRE(td.module->o_empty == 1); 216 | REQUIRE(td.module->o_full == 0); 217 | REQUIRE(td.module->o_item_count == 0); 218 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT); 219 | 220 | // Write until FIFO full 221 | uint16_t w_data[FIFO_MAX_ITEM_COUNT]; 222 | int i = 0; 223 | while (!td.module->o_full) { 224 | // Write a random 16-bit word to the fifo 225 | td.module->i_clk = 1; 226 | td.module->i_w_data = rand() & 0xFFFF; 227 | w_data[i] = td.module->i_w_data; 228 | CAPTURE(td.module->i_w_data); 229 | td.module->i_w_data_stb = 1; 230 | td.eval(); 231 | 232 | // Assert the size has increased 233 | i++; 234 | REQUIRE(td.module->o_empty == 0); 235 | REQUIRE(td.module->o_item_count == i); 236 | REQUIRE(td.module->o_free_size == FIFO_MAX_ITEM_COUNT - i); 237 | td.module->i_clk = 0; 238 | td.eval(); 239 | } 240 | 241 | // Assert full 242 | REQUIRE(td.module->o_empty == 0); 243 | REQUIRE(td.module->o_full == 1); 244 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT); 245 | REQUIRE(td.module->o_free_size == 0); 246 | 247 | // Write again 248 | td.module->i_clk = 1; 249 | td.module->i_w_data = rand() & 0xFFFF; 250 | td.module->i_w_data_stb = 1; 251 | td.eval(); 252 | 253 | // Assert still full 254 | REQUIRE(td.module->o_empty == 0); 255 | REQUIRE(td.module->o_full == 1); 256 | REQUIRE(td.module->o_item_count == FIFO_MAX_ITEM_COUNT); 257 | REQUIRE(td.module->o_free_size == 0); 258 | } 259 | -------------------------------------------------------------------------------- /test/test_main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include 3 | -------------------------------------------------------------------------------- /test/test_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | /* 14 | * This file contains some templated structures that make writing tests a bit 15 | * more convenient. These structures assume that all modules follow a naming 16 | * convention that has a primary clock called i_clk, and active-high reset 17 | * called i_reset. 18 | * The Wishbone version of the test class also assumes that the 19 | * interface matches a common naming pattern. 20 | */ 21 | 22 | template ::value>::type * = nullptr> 24 | struct TestData { 25 | // Verilated module 26 | T *module; 27 | 28 | // Trace context 29 | VerilatedVcdC *trace; 30 | 31 | // Current time tick 32 | uint64_t time_ns = 0; 33 | 34 | // Evaluate the module and write a new trace entry 35 | void eval() { 36 | module->eval(); 37 | trace->dump(time_ns++); 38 | } 39 | 40 | // Creates a new test data with a given trace output file 41 | TestData(const char *trace_name) { 42 | Verilated::traceEverOn(true); 43 | module = new T; 44 | trace = new VerilatedVcdC(); 45 | module->trace(trace, 99); 46 | trace->open(trace_name); 47 | } 48 | 49 | ~TestData() { 50 | if (module != nullptr) { 51 | free(module); 52 | } 53 | if (trace != nullptr) { 54 | trace->flush(); 55 | trace->close(); 56 | } 57 | } 58 | 59 | // Clock the module for N half-clock cycles 60 | void idle_clock(int count) { 61 | while (count--) { 62 | module->i_clk = module->i_clk ? 0 : 1; 63 | eval(); 64 | } 65 | } 66 | 67 | // Perform a reset of the module 68 | void reset() { 69 | module->i_clk = 0; 70 | module->i_reset = 1; 71 | eval(); 72 | module->i_clk = 1; 73 | eval(); 74 | module->i_clk = 0; 75 | module->i_reset = 0; 76 | eval(); 77 | } 78 | }; 79 | 80 | template struct WishboneTestData : public TestData { 81 | 82 | using TestData::TestData; 83 | 84 | uint32_t wb_read(uint32_t address, int timeout = 32) { 85 | // Assert we were called at negedge 86 | REQUIRE(this->module->i_clk == 0); 87 | 88 | // Set up read 89 | this->module->i_wb_adr = address; 90 | this->module->i_wb_dat = 0x0; 91 | this->module->i_wb_sel = 0b1111; 92 | this->module->i_wb_we = 0; 93 | this->module->i_wb_cyc = 1; 94 | this->module->i_wb_stb = 1; 95 | 96 | // Latch read 97 | this->module->i_clk = 1; 98 | this->eval(); 99 | 100 | // Clock until ack goes high 101 | while (!this->module->o_wb_ack && timeout--) { 102 | this->module->i_clk = this->module->i_clk ? 0 : 1; 103 | this->eval(); 104 | } 105 | REQUIRE(this->module->o_wb_ack == 1); 106 | 107 | // Clear wb signals 108 | this->module->i_wb_adr = 0; 109 | this->module->i_wb_dat = 0; 110 | this->module->i_wb_sel = 0; 111 | this->module->i_wb_cyc = 0; 112 | this->module->i_wb_stb = 0; 113 | 114 | // Restore clock to low 115 | this->module->i_clk = 0; 116 | this->eval(); 117 | 118 | // Return data 119 | return this->module->o_wb_dat; 120 | } 121 | 122 | void wb_write(uint32_t address, uint32_t data, uint8_t sel = 0b1111, 123 | int timeout = 32) { 124 | // Assert we were called at negedge 125 | REQUIRE(this->module->i_clk == 0); 126 | 127 | // Set up read 128 | this->module->i_wb_adr = address; 129 | this->module->i_wb_dat = data; 130 | this->module->i_wb_sel = sel; 131 | this->module->i_wb_we = 1; 132 | this->module->i_wb_cyc = 1; 133 | this->module->i_wb_stb = 1; 134 | 135 | // Latch read 136 | this->module->i_clk = 1; 137 | this->eval(); 138 | 139 | // Clock until ack goes high 140 | while (!this->module->o_wb_ack && timeout--) { 141 | this->module->i_clk = this->module->i_clk ? 0 : 1; 142 | this->eval(); 143 | } 144 | REQUIRE(this->module->o_wb_ack == 1); 145 | 146 | // Clear wb signals 147 | this->module->i_wb_adr = 0; 148 | this->module->i_wb_dat = 0; 149 | this->module->i_wb_sel = 0; 150 | this->module->i_wb_cyc = 0; 151 | this->module->i_wb_stb = 0; 152 | 153 | // Restore clock to low 154 | this->module->i_clk = 0; 155 | this->eval(); 156 | } 157 | }; 158 | -------------------------------------------------------------------------------- /test/wb_ram_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_utils.hpp" 4 | 5 | #include 6 | 7 | static const int RAM_SIZE_WORDS = 512; 8 | 9 | TEST_CASE("Read/write with sel test", "[MEMORY]") { 10 | WishboneTestData td("wb_ram_test.vcd"); 11 | 12 | // Write some data to address zero 13 | td.wb_write(0x0, 0xDEADBEEF); 14 | 15 | // Assert that we may read it back 16 | REQUIRE(td.wb_read(0x0) == 0xDEADBEEF); 17 | 18 | // Write something else to the next 32-bit word address 19 | td.wb_write(0x4, 0xFA73C0DE); 20 | REQUIRE(td.wb_read(0x4) == 0xFA73C0DE); 21 | 22 | // The second write should not have affected our first write 23 | REQUIRE(td.wb_read(0x0) == 0xDEADBEEF); 24 | 25 | // Write a new word, but only select the lower two bytes 26 | td.wb_write(0x0, 0xFEEDCAFE, 0b0011); 27 | 28 | // Assert that when we read the data back, only the low bytes changed 29 | REQUIRE(td.wb_read(0x0) == 0xDEADCAFE); 30 | 31 | // Check the same is true for the high bytes 32 | td.wb_write(0x0, 0xFACEF00D, 0b1100); 33 | REQUIRE(td.wb_read(0x0) == 0xFACECAFE); 34 | } 35 | -------------------------------------------------------------------------------- /vendor/catch/ParseAndAddCatchTests.cmake: -------------------------------------------------------------------------------- 1 | #==================================================================================================# 2 | # supported macros # 3 | # - TEST_CASE, # 4 | # - SCENARIO, # 5 | # - TEST_CASE_METHOD, # 6 | # - CATCH_TEST_CASE, # 7 | # - CATCH_SCENARIO, # 8 | # - CATCH_TEST_CASE_METHOD. # 9 | # # 10 | # Usage # 11 | # 1. make sure this module is in the path or add this otherwise: # 12 | # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # 13 | # 2. make sure that you've enabled testing option for the project by the call: # 14 | # enable_testing() # 15 | # 3. add the lines to the script for testing target (sample CMakeLists.txt): # 16 | # project(testing_target) # 17 | # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # 18 | # enable_testing() # 19 | # # 20 | # find_path(CATCH_INCLUDE_DIR "catch.hpp") # 21 | # include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # 22 | # # 23 | # file(GLOB SOURCE_FILES "*.cpp") # 24 | # add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # 25 | # # 26 | # include(ParseAndAddCatchTests) # 27 | # ParseAndAddCatchTests(${PROJECT_NAME}) # 28 | # # 29 | # The following variables affect the behavior of the script: # 30 | # # 31 | # PARSE_CATCH_TESTS_VERBOSE (Default OFF) # 32 | # -- enables debug messages # 33 | # PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # 34 | # -- excludes tests marked with [!hide], [.] or [.foo] tags # 35 | # PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # 36 | # -- adds fixture class name to the test name # 37 | # PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # 38 | # -- adds cmake target name to the test name # 39 | # PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # 40 | # -- causes CMake to rerun when file with tests changes so that new tests will be discovered # 41 | # # 42 | # One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way # 43 | # a test should be run. For instance to use test MPI, one can write # 44 | # set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) # 45 | # just before calling this ParseAndAddCatchTests function # 46 | # # 47 | # The AdditionalCatchParameters optional variable can be used to pass extra argument to the test # 48 | # command. For example, to include successful tests in the output, one can write # 49 | # set(AdditionalCatchParameters --success) # 50 | # # 51 | # After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source # 52 | # file in the target is set, and contains the list of the tests extracted from that target, or # 53 | # from that file. This is useful, for example to add further labels or properties to the tests. # 54 | # # 55 | #==================================================================================================# 56 | 57 | if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8) 58 | message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer") 59 | endif() 60 | 61 | option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) 62 | option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) 63 | option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) 64 | option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) 65 | option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) 66 | 67 | function(ParseAndAddCatchTests_PrintDebugMessage) 68 | if(PARSE_CATCH_TESTS_VERBOSE) 69 | message(STATUS "ParseAndAddCatchTests: ${ARGV}") 70 | endif() 71 | endfunction() 72 | 73 | # This removes the contents between 74 | # - block comments (i.e. /* ... */) 75 | # - full line comments (i.e. // ... ) 76 | # contents have been read into '${CppCode}'. 77 | # !keep partial line comments 78 | function(ParseAndAddCatchTests_RemoveComments CppCode) 79 | string(ASCII 2 CMakeBeginBlockComment) 80 | string(ASCII 3 CMakeEndBlockComment) 81 | string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") 82 | string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") 83 | string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") 84 | string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") 85 | 86 | set(${CppCode} "${${CppCode}}" PARENT_SCOPE) 87 | endfunction() 88 | 89 | # Worker function 90 | function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) 91 | # If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file. 92 | if(SourceFile MATCHES "\\\$") 93 | ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.") 94 | return() 95 | endif() 96 | # According to CMake docs EXISTS behavior is well-defined only for full paths. 97 | get_filename_component(SourceFile ${SourceFile} ABSOLUTE) 98 | if(NOT EXISTS ${SourceFile}) 99 | message(WARNING "Cannot find source file: ${SourceFile}") 100 | return() 101 | endif() 102 | ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}") 103 | file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) 104 | 105 | # Remove block and fullline comments 106 | ParseAndAddCatchTests_RemoveComments(Contents) 107 | 108 | # Find definition of test names 109 | string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") 110 | 111 | if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) 112 | ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") 113 | set_property( 114 | DIRECTORY 115 | APPEND 116 | PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} 117 | ) 118 | endif() 119 | 120 | foreach(TestName ${Tests}) 121 | # Strip newlines 122 | string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") 123 | 124 | # Get test type and fixture if applicable 125 | string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") 126 | string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") 127 | string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}") 128 | 129 | # Get string parts of test definition 130 | string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") 131 | 132 | # Strip wrapping quotation marks 133 | string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") 134 | string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") 135 | 136 | # Validate that a test name and tags have been provided 137 | list(LENGTH TestStrings TestStringsLength) 138 | if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) 139 | message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") 140 | endif() 141 | 142 | # Assign name and tags 143 | list(GET TestStrings 0 Name) 144 | if("${TestType}" STREQUAL "SCENARIO") 145 | set(Name "Scenario: ${Name}") 146 | endif() 147 | if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture ) 148 | set(CTestName "${TestFixture}:${Name}") 149 | else() 150 | set(CTestName "${Name}") 151 | endif() 152 | if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) 153 | set(CTestName "${TestTarget}:${CTestName}") 154 | endif() 155 | # add target to labels to enable running all tests added from this target 156 | set(Labels ${TestTarget}) 157 | if(TestStringsLength EQUAL 2) 158 | list(GET TestStrings 1 Tags) 159 | string(TOLOWER "${Tags}" Tags) 160 | # remove target from labels if the test is hidden 161 | if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") 162 | list(REMOVE_ITEM Labels ${TestTarget}) 163 | endif() 164 | string(REPLACE "]" ";" Tags "${Tags}") 165 | string(REPLACE "[" "" Tags "${Tags}") 166 | else() 167 | # unset tags variable from previous loop 168 | unset(Tags) 169 | endif() 170 | 171 | list(APPEND Labels ${Tags}) 172 | 173 | set(HiddenTagFound OFF) 174 | foreach(label ${Labels}) 175 | string(REGEX MATCH "^!hide|^\\." result ${label}) 176 | if(result) 177 | set(HiddenTagFound ON) 178 | break() 179 | endif(result) 180 | endforeach(label) 181 | if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") 182 | ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") 183 | else() 184 | ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"") 185 | if(Labels) 186 | ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}") 187 | endif() 188 | 189 | # Escape commas in the test spec 190 | string(REPLACE "," "\\," Name ${Name}) 191 | 192 | # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were neccessary, 193 | # only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1 194 | if(NOT ${CMAKE_VERSION} VERSION_EQUAL "3.18") 195 | set(CTestName "\"${CTestName}\"") 196 | endif() 197 | # Add the test and set its properties 198 | add_test(NAME "${CTestName}" COMMAND ${OptionalCatchTestLauncher} $ ${Name} ${AdditionalCatchParameters}) 199 | # Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead 200 | if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8") 201 | ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property") 202 | set_tests_properties("${CTestName}" PROPERTIES DISABLED ON) 203 | else() 204 | set_tests_properties("${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" 205 | LABELS "${Labels}") 206 | endif() 207 | set_property( 208 | TARGET ${TestTarget} 209 | APPEND 210 | PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") 211 | set_property( 212 | SOURCE ${SourceFile} 213 | APPEND 214 | PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") 215 | endif() 216 | 217 | 218 | endforeach() 219 | endfunction() 220 | 221 | # entry point 222 | function(ParseAndAddCatchTests TestTarget) 223 | ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}") 224 | get_target_property(SourceFiles ${TestTarget} SOURCES) 225 | ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}") 226 | foreach(SourceFile ${SourceFiles}) 227 | ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget}) 228 | endforeach() 229 | ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}") 230 | endfunction() 231 | -------------------------------------------------------------------------------- /vendor/vexriscv/GenVexRiscv.scala: -------------------------------------------------------------------------------- 1 | package vexriscv.demo 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | import spinal.lib.eda.altera.{InterruptReceiverTag, QSysify, ResetEmitterTag} 6 | import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} 7 | import vexriscv.plugin._ 8 | import vexriscv.{VexRiscv, VexRiscvConfig, plugin} 9 | 10 | object GenVexRiscv{ 11 | def main(args: Array[String]) { 12 | val report = SpinalVerilog{ 13 | 14 | // CPU configuration 15 | val cpuConfig = VexRiscvConfig( 16 | plugins = List( 17 | 18 | // We need an instruction data bus on the CPU. This bus is separate 19 | // from the data bus for performance reasons, and here we will 20 | // instantiate the cached version of this plugin, which is a 21 | // significant performance improvement on a non-cached implementation 22 | new IBusCachedPlugin( 23 | // We want to be able to set the reset address in verilog later, so 24 | // leave it null here 25 | resetVector = null, 26 | // Conditional branches are speculatively executed. 27 | // There is no tracking of whether a branch is more likely to be 28 | // executed or not 29 | prediction = STATIC, 30 | // Include a 4KiB instruction cache 31 | config = InstructionCacheConfig( 32 | cacheSize = 4096, 33 | bytePerLine = 32, 34 | wayCount = 1, 35 | addressWidth = 32, 36 | cpuDataWidth = 32, 37 | memDataWidth = 32, 38 | catchIllegalAccess = true, 39 | catchAccessFault = true, 40 | asyncTagMemory = false, 41 | twoCycleRam = true 42 | ) 43 | ), 44 | 45 | // Data bus. We don't add a cache here so that we don't need to worry 46 | // about write coalescing to MMIO regions 47 | new DBusSimplePlugin( 48 | catchAddressMisaligned = true, 49 | catchAccessFault = true 50 | ), 51 | 52 | // Instruction decoding 53 | new DecoderSimplePlugin( 54 | catchIllegalInstruction = true 55 | ), 56 | 57 | // Register file 58 | new RegFilePlugin( 59 | regFileReadyKind = plugin.SYNC, 60 | zeroBoot = false 61 | ), 62 | 63 | // Handles ADD/SUB/SLT/SLTU/XOR/OR/AND/LUI/AUIPC 64 | new IntAluPlugin, 65 | 66 | // Generates SRC1/SRC2/SRC_ADD/SRC_SUB/SRC_LESS 67 | new SrcPlugin( 68 | separatedAddSub = false, 69 | // Relax bypassing 70 | executeInsertion = true 71 | ), 72 | 73 | // Implements SLL/SRL/SRA using an iterative shift register. 74 | // For applications that do a lot of shifts, one may want to 75 | // instantiate the FullBarrelShifterPlugin instead 76 | new LightShifterPlugin, 77 | 78 | // Iterative implementation of multiply/divide instructions. 79 | // For faster execution, the unroll factor may be increased at the 80 | // cost of more logic. 81 | // For FPGAs with DSP blocks, for faster mul/div you can instantiate 82 | // the MulPlugin and DivPlugin instead 83 | new MulDivIterativePlugin( 84 | genMul = true, 85 | genDiv = true, 86 | mulUnrollFactor = 1, 87 | divUnrollFactor = 1 88 | ), 89 | 90 | // Checks the pipeline instruction dependencies and, if necessary 91 | // or possible, will stop the instruction in the decoding stage or 92 | // bypass the instruction results from the later stages of the 93 | // decode stage. 94 | new HazardSimplePlugin( 95 | bypassExecute = true, 96 | bypassMemory = true, 97 | bypassWriteBack = true, 98 | bypassWriteBackBuffer = true, 99 | pessimisticUseSrc = false, 100 | pessimisticWriteRegFile = false, 101 | pessimisticAddressMatch = false 102 | ), 103 | 104 | // Handles JAL/JALR/BEQ/BNE/BLT/BGE/BLTU/BGEU 105 | new BranchPlugin( 106 | // If early branch is true, branch will be done in the execute stage 107 | // This is faster but hurts timings 108 | earlyBranch = false, 109 | // We want to trap if the CPU tries to jump to a misaligned address 110 | catchAddressMisaligned = true 111 | ), 112 | 113 | // Implementation of the Control and Status Registers. 114 | // We want to make sure that registers we use for interrupts, such as 115 | // mtvec and mcause, are accessible. We have also enabled mcycle 116 | // access for performance timing. 117 | new CsrPlugin( 118 | config = CsrPluginConfig( 119 | catchIllegalAccess = false, 120 | mvendorid = null, 121 | marchid = null, 122 | mimpid = null, 123 | mhartid = null, 124 | misaExtensionsInit = 66, 125 | misaAccess = CsrAccess.NONE, 126 | mtvecAccess = CsrAccess.READ_WRITE, 127 | mtvecInit = 0x80000000l, 128 | xtvecModeGen = true, 129 | mepcAccess = CsrAccess.READ_WRITE, 130 | mscratchGen = false, 131 | mcauseAccess = CsrAccess.READ_ONLY, 132 | mbadaddrAccess = CsrAccess.READ_ONLY, 133 | mcycleAccess = CsrAccess.READ_WRITE, 134 | minstretAccess = CsrAccess.NONE, 135 | ecallGen = false, 136 | wfiGenAsWait = false, 137 | ucycleAccess = CsrAccess.READ_ONLY, 138 | uinstretAccess = CsrAccess.NONE 139 | ) 140 | ), 141 | 142 | new YamlPlugin("cpu0.yaml") 143 | ) 144 | ) 145 | 146 | // CPU instantiation 147 | val cpu = new VexRiscv(cpuConfig) 148 | 149 | // CPU modifications to use a wishbone interface 150 | cpu.rework { 151 | for (plugin <- cpuConfig.plugins) plugin match { 152 | case plugin: IBusSimplePlugin => { 153 | plugin.iBus.setAsDirectionLess() 154 | master(plugin.iBus.toWishbone()).setName("iBusWishbone") 155 | } 156 | case plugin: IBusCachedPlugin => { 157 | plugin.iBus.setAsDirectionLess() 158 | master(plugin.iBus.toWishbone()).setName("iBusWishbone") 159 | } 160 | case plugin: DBusSimplePlugin => { 161 | plugin.dBus.setAsDirectionLess() 162 | master(plugin.dBus.toWishbone()).setName("dBusWishbone") 163 | } 164 | case plugin: DBusCachedPlugin => { 165 | plugin.dBus.setAsDirectionLess() 166 | master(plugin.dBus.toWishbone()).setName("dBusWishbone") 167 | } 168 | case _ => 169 | } 170 | } 171 | 172 | cpu 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /wb_intercon.yaml: -------------------------------------------------------------------------------- 1 | vlnv: ::soc_wb 2 | files_root: blah 3 | parameters: 4 | masters: 5 | cpu0_ibus: 6 | slaves: [cpu0_rom] 7 | 8 | cpu0_dbus: 9 | slaves: [cpu0_rom, cpu0_ram, 10 | timer0, 11 | uart0, 12 | led0] 13 | 14 | slaves: 15 | timer0: 16 | offset: 0x40000000 17 | size: 0x1000 18 | 19 | uart0: 20 | offset: 0x40001000 21 | size: 0x1000 22 | 23 | led0: 24 | offset: 0x40002000 25 | size: 0x1000 26 | 27 | cpu0_rom: 28 | offset: 0x20000000 29 | size: 0x1000 30 | 31 | cpu0_ram: 32 | offset: 0x80000000 33 | size: 0x800 34 | --------------------------------------------------------------------------------