├── .github └── workflows │ ├── CI.yml │ ├── formatter.yml │ └── linter.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── design ├── wfg_core │ ├── README.md │ ├── data │ │ ├── wfg_core_reg.csv │ │ └── wfg_core_reg.json │ ├── formula.png │ ├── rtl │ │ ├── wfg_core.sv │ │ ├── wfg_core_top.sv │ │ └── wfg_core_wishbone_reg.sv │ ├── sim │ │ ├── Makefile │ │ └── view.gtkw │ └── testbench │ │ ├── test_wfg_core.py │ │ └── wfg_core_tb.sv ├── wfg_drive_pat │ ├── README.md │ ├── data │ │ ├── wfg_drive_pat_reg.csv │ │ └── wfg_drive_pat_reg.json │ ├── rtl │ │ ├── wfg_drive_pat.sv │ │ ├── wfg_drive_pat_channel.sv │ │ ├── wfg_drive_pat_top.sv │ │ └── wfg_drive_pat_wishbone_reg.sv │ ├── sim │ │ └── Makefile │ └── testbench │ │ ├── test_wfg_drive_pat.py │ │ └── wfg_drive_pat_tb.sv ├── wfg_drive_spi │ ├── README.md │ ├── data │ │ ├── wfg_drive_spi_reg.csv │ │ └── wfg_drive_spi_reg.json │ ├── rtl │ │ ├── wfg_drive_spi.sv │ │ ├── wfg_drive_spi_top.sv │ │ └── wfg_drive_spi_wishbone_reg.sv │ ├── sim │ │ ├── Makefile │ │ └── view.gtkw │ └── testbench │ │ ├── test_wfg_drive_spi.py │ │ └── wfg_drive_spi_tb.sv ├── wfg_interconnect │ ├── data │ │ ├── wfg_interconnect_reg.csv │ │ └── wfg_interconnect_reg.json │ ├── rtl │ │ ├── wfg_interconnect.sv │ │ ├── wfg_interconnect_pkg.svh │ │ ├── wfg_interconnect_top.sv │ │ └── wfg_interconnect_wishbone_reg.sv │ ├── sim │ │ ├── Makefile │ │ └── view.gtkw │ └── testbench │ │ ├── test_wfg_interconnect.py │ │ └── wfg_interconnect_tb.sv ├── wfg_stim_mem │ ├── data │ │ ├── wfg_stim_mem_reg.csv │ │ └── wfg_stim_mem_reg.json │ ├── rtl │ │ ├── dsp_scale_sn_us.sv │ │ ├── wfg_stim_mem.sv │ │ ├── wfg_stim_mem_top.sv │ │ └── wfg_stim_mem_wishbone_reg.sv │ ├── sim │ │ ├── Makefile │ │ └── memory.hex │ └── testbench │ │ ├── test_wfg_stim_mem.py │ │ └── wfg_stim_mem_tb.sv ├── wfg_stim_sine │ ├── README.md │ ├── data │ │ ├── wfg_stim_sine_reg.csv │ │ └── wfg_stim_sine_reg.json │ ├── rtl │ │ ├── wfg_stim_sine.sv │ │ ├── wfg_stim_sine_top.sv │ │ └── wfg_stim_sine_wishbone_reg.sv │ ├── sim │ │ └── Makefile │ └── testbench │ │ ├── test_wfg_stim_sine.py │ │ └── wfg_stim_sine_tb.sv ├── wfg_subcore │ ├── README.md │ ├── data │ │ ├── wfg_subcore_reg.csv │ │ └── wfg_subcore_reg.json │ ├── formula.png │ ├── rtl │ │ ├── wfg_subcore.sv │ │ ├── wfg_subcore_top.sv │ │ └── wfg_subcore_wishbone_reg.sv │ ├── sim │ │ ├── Makefile │ │ └── view.gtkw │ └── testbench │ │ ├── test_wfg_core.py │ │ ├── test_wfg_subcore.py │ │ └── wfg_subcore_tb.sv └── wfg_top │ ├── rtl │ └── wfg_top.sv │ ├── sim │ ├── Makefile │ ├── memory.hex │ ├── output_inc=1280_gain=16384_off=0.png │ ├── output_inc=1280_gain=16384_off=0.svg │ ├── output_inc=4096_gain=16384_off=0.png │ ├── output_inc=4096_gain=16384_off=0.svg │ └── view.gtkw │ └── testbench │ ├── test_wfg_top.py │ └── wfg_top_tb.sv ├── fpga ├── ulx3s_barebones │ ├── ulx3s_top.sv │ └── ulx3s_v20.lpf └── ulx3s_soc │ ├── Makefile │ ├── analyzer.csv │ ├── csr.csv │ ├── femtorv32_quark.v │ ├── firmware │ ├── build.make │ ├── donut.c │ ├── firmware_build.bin │ ├── firmware_build.elf │ ├── firmware_sim.bin │ ├── firmware_sim.elf │ ├── linker.ld │ ├── main.c │ ├── sim.c │ ├── sim.make │ ├── sim_debug.c │ ├── sim_debug.h │ └── wfg.c │ ├── radiona_ulx3s.py │ └── ulx3s_soc.py ├── img ├── WFG.png ├── litex.png └── utilization.png ├── requirements.txt └── templating ├── converter.py ├── generator.py └── templates └── wishbone ├── assign_from_registers.template ├── assign_outputs.template ├── assign_to_module.template ├── assign_to_registers.template ├── instantiate_registers.template ├── instantiate_top.template ├── register_interface.template ├── register_overview.template └── reset_registers.template /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: [push] 3 | jobs: 4 | tests: 5 | runs-on: ubuntu-latest 6 | timeout-minutes: 10 7 | 8 | steps: 9 | - uses: actions/checkout@main 10 | - name: Compile and install iverilog 11 | run: | 12 | sudo apt install -y gperf 13 | git clone https://github.com/steveicarus/iverilog.git 14 | cd iverilog 15 | sh autoconf.sh 16 | ./configure 17 | make -j$(nproc) 18 | sudo make install 19 | - name: Install cocotb and modules 20 | run: | 21 | cd ~/work/waveform-generator/waveform-generator/ 22 | pip3 install cocotb 23 | pip3 install cocotbext-wishbone 24 | pip3 install cocotbext-axi 25 | pip3 install cocotbext-spi 26 | pip3 install matplotlib 27 | pip3 install scipy 28 | pip3 install numpy 29 | - name: Run tests 30 | run: | 31 | cd ~/work/waveform-generator/waveform-generator/ 32 | make templates 33 | make tests 34 | - name: Report failures 35 | run: | 36 | ! grep failure design/*/sim/results.xml 37 | -------------------------------------------------------------------------------- /.github/workflows/formatter.yml: -------------------------------------------------------------------------------- 1 | name: Verible formatter 2 | on: 3 | pull_request: 4 | jobs: 5 | format: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - uses: chipsalliance/verible-formatter-action@main 10 | with: 11 | parameters: 12 | --indentation_spaces 4 13 | --module_net_variable_alignment=preserve 14 | --case_items_alignment=preserve 15 | github_token: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: Verible linter 2 | on: [push] 3 | jobs: 4 | linter: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@master 8 | - uses: chipsalliance/verible-linter-action@main 9 | with: 10 | github_token: ${{ secrets.GITHUB_TOKEN }} 11 | config_file: 'config.rules' 12 | fail_on_error: true 13 | suggest_fixes: 'false' 14 | paths: | 15 | ./design/ 16 | extra_args: "--check_syntax=true --rules=-unpacked-dimensions-range-ordering" 17 | - name: Log output 18 | run: cat verible-verilog-lint.log 19 | - name: Upload artifact 20 | uses: actions/upload-artifact@main 21 | with: 22 | name: linter-log 23 | path: verible-verilog-lint.log 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache 2 | **/__pycache__ 3 | **/sim_build 4 | 5 | # Dump 6 | *.vcd 7 | *.fst 8 | 9 | results.xml 10 | .~lock.* 11 | *.log 12 | *.bit 13 | *.config 14 | ulx3s.json 15 | 16 | build 17 | *.o 18 | *.d 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := all 2 | 3 | all: templates 4 | 5 | TEMPLATED_FILES := design/wfg_stim_sine/rtl/wfg_stim_sine_wishbone_reg.sv \ 6 | design/wfg_stim_sine/rtl/wfg_stim_sine_top.sv \ 7 | design/wfg_stim_mem/rtl/wfg_stim_mem_wishbone_reg.sv \ 8 | design/wfg_stim_mem/rtl/wfg_stim_mem_top.sv \ 9 | design/wfg_drive_spi/rtl/wfg_drive_spi_top.sv \ 10 | design/wfg_drive_spi/rtl/wfg_drive_spi_wishbone_reg.sv \ 11 | design/wfg_drive_pat/rtl/wfg_drive_pat_top.sv \ 12 | design/wfg_drive_pat/rtl/wfg_drive_pat_wishbone_reg.sv \ 13 | design/wfg_interconnect/rtl/wfg_interconnect_top.sv \ 14 | design/wfg_interconnect/rtl/wfg_interconnect_wishbone_reg.sv \ 15 | design/wfg_core/rtl/wfg_core_top.sv \ 16 | design/wfg_core/rtl/wfg_core_wishbone_reg.sv \ 17 | design/wfg_subcore/rtl/wfg_subcore_top.sv \ 18 | design/wfg_subcore/rtl/wfg_subcore_wishbone_reg.sv 19 | 20 | 21 | DATA_FILES := design/wfg_stim_sine/data/wfg_stim_sine_reg.csv \ 22 | design/wfg_stim_mem/data/wfg_stim_mem_reg.csv \ 23 | design/wfg_drive_spi/data/wfg_drive_spi_reg.csv \ 24 | design/wfg_drive_pat/data/wfg_drive_pat_reg.csv \ 25 | design/wfg_interconnect/data/wfg_interconnect_reg.csv \ 26 | design/wfg_core/data/wfg_core_reg.csv \ 27 | design/wfg_subcore/data/wfg_subcore_reg.csv 28 | 29 | LIBRARIES := $(DATA_FILES:.csv=.json) 30 | 31 | %.json: %.csv 32 | python3 templating/converter.py -i $^ -o $@ 33 | 34 | templates: ${TEMPLATED_FILES} ${LIBRARIES} 35 | python3 templating/generator.py --template_dir templating/templates -i ${TEMPLATED_FILES} 36 | 37 | tests: 38 | cd design/wfg_stim_sine/sim; make sim 39 | cd design/wfg_stim_mem/sim; make sim 40 | cd design/wfg_drive_spi/sim; make sim 41 | #cd design/wfg_drive_pat/sim; make sim # TODO fix tests 42 | cd design/wfg_interconnect/sim; make sim 43 | cd design/wfg_core/sim; make sim 44 | cd design/wfg_subcore/sim; make sim 45 | cd design/wfg_top/sim; make sim 46 | 47 | lint: 48 | verible-verilog-lint --rules=-unpacked-dimensions-range-ordering design/*/*/*.sv 49 | 50 | lint-verilator: 51 | verilator --lint-only -Wall design/wfg_drive_spi/rtl/*.sv 52 | 53 | lint-autofix: 54 | verible-verilog-lint --rules=-unpacked-dimensions-range-ordering --autofix inplace-interactive design/*/*/*.sv 55 | 56 | format: 57 | verible-verilog-format --indentation_spaces 4 --module_net_variable_alignment=preserve --case_items_alignment=preserve design/*/*/*.sv --inplace --verbose 58 | 59 | ulx3s.json: design/*/rtl/*.sv 60 | yosys -ql $(basename $@)-yosys.log -p 'synth_ecp5 -top wfg_top -json $@' $^ 61 | 62 | nextpnr-view: ulx3s.json 63 | nextpnr-ecp5 --85k --json $< \ 64 | --lpf-allow-unconstrained \ 65 | --package CABGA381 \ 66 | --textcfg ulx3s_out.config --gui 67 | 68 | clean: 69 | rm -rf design/*/sim/sim_build 70 | rm -rf design/*/sim/*.vcd 71 | rm -rf design/*/sim/*.xml 72 | rm -f ulx3s_out.config 73 | rm -f ulx3s-yosys.log 74 | rm -f ulx3s.json 75 | 76 | .PHONY: templates unit-tests lint lint-autofix format clean nextpnr_view 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Waveform Generator [![Continuous Integration](https://github.com/semify-eda/waveform-generator/actions/workflows/CI.yml/badge.svg)](https://github.com/semify-eda/waveform-generator/actions/workflows/CI.yml) 2 | 3 | This repository implements a generic Waveform Generator in SystemVerilog. 4 | 5 | ## Introduction 6 | 7 | The Waveform Generator is split up into three parts: 8 | 9 | - Stimuli 10 | - Interconnect 11 | - Drivers 12 | 13 | ![Waveform Generator](img/WFG.png) 14 | 15 | Currently the following components are available: 16 | 17 | - `wfg_core` 18 | - `wfg_subcore` 19 | - `wfg_interconnect` 20 | - `wfg_stim_sine` 21 | - `wfg_stim_mem` 22 | - `wfg_drive_spi` 23 | - `wfg_drive_pat` 24 | 25 | ## Prerequisites 26 | 27 | For this project you will need Python3+ and pip. 28 | 29 | For the generation of the register files based on templates install `jinja2`: 30 | 31 | pip3 install jinja2 32 | 33 | The testbench environment for the unit-tests uses `cocotb`. To install it together with the bus interfaces, run: 34 | 35 | pip3 install cocotb 36 | pip3 install cocotbext-axi 37 | pip3 install cocotbext-spi 38 | pip3 install cocotbext-wishbone 39 | 40 | To plot the values during functional verification install the following modules: 41 | 42 | pip3 install scipy 43 | pip3 install numpy 44 | pip3 install matplotlib 45 | 46 | To get more information on assertion fails you can optionally install `pytest`: 47 | 48 | pip3 install pytest 49 | 50 | ## Simulation 51 | 52 | To run the individual unit tests, issue: 53 | 54 | make tests 55 | 56 | ## Template Based Generation 57 | 58 | To generate the register files for the wishbone bus, issue: 59 | 60 | make templates 61 | 62 | ## Code Formatting 63 | 64 | To ensure consistent formatting, [verible](https://github.com/chipsalliance/verible) is used as a SystemVerilog formatter tool. 65 | 66 | make format 67 | 68 | This will format the code according to some custom flags. 69 | 70 | ## Invoke Linter 71 | 72 | To invoke the [verible](https://github.com/chipsalliance/verible) linter, run: 73 | 74 | make lint 75 | 76 | ## FPGA Prototyping with LiteX 77 | 78 | To run the project on an FPGA, [LiteX](https://github.com/enjoy-digital/litex) is used to instantiate the SoC. 79 | 80 | Follow the instructions on the GitHub page to install LiteX. After installation don't forget to add the RISC-V toolchain to the PATH variable. 81 | 82 | To automatically generate documentation about the SoC, install: 83 | 84 | pip3 install sphinx sphinxcontrib-wavedrom 85 | 86 | To trace the simulation using `--trace` you need to install: 87 | 88 | pip3 install pyvcd 89 | 90 | ### Simulation 91 | 92 | To run the simulation with LiteX go to: 93 | 94 | `fpga/ulx3s_soc` 95 | 96 | First run 97 | 98 | make sim-prebuild 99 | 100 | and after that finally: 101 | 102 | make sim 103 | 104 | The output of the simulation window should be: 105 | 106 | ![LiteX simulation](img/litex.png) 107 | 108 | ### FPGA Bitstream generation 109 | 110 | To build the bitstream with LiteX for the ULX3S go to: 111 | 112 | `fpga/ulx3s_soc` 113 | 114 | First run 115 | 116 | make build-prebuild 117 | 118 | and after that finally: 119 | 120 | make build 121 | 122 | To upload the bitstream to the ULX3S board, run: 123 | 124 | make only-upload 125 | 126 | ## View FPGA Utilization 127 | 128 | To view the FPGA utilization of only the Waveform Generator with nextpnr, issue the command: 129 | 130 | nextpnr-view 131 | 132 | Next, a window will open. In the top menu, choose the following actions one after another: 133 | 134 | - Pack 135 | - Place 136 | - Route 137 | 138 | After this you can just take a screenshot of the finished design. 139 | 140 | It will look like this: 141 | 142 | ![ULX3S Utilization](img/utilization.png) 143 | 144 | ## License 145 | 146 | Copyright [semify](https://www.semify-eda.com/). 147 | 148 | Unless otherwise specified, source code in this repository is licensed under the Apache License Version 2.0 (Apache-2.0). A copy is included in the LICENSE file. 149 | 150 | Other licenses may be specified as well for certain files for purposes of illustration or where third-party components are used. 151 | -------------------------------------------------------------------------------- /design/wfg_core/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | *Copyright © 2021* [Semify EDA]( 4 | https://github.com/semify-eda) 5 | 6 | ## wfg_core documentation 7 | #### Overview 8 | This module is responsible to synchronize all implemented modules, ensuring data transfers with a set frequency. 9 | According to the configuration parameter wfg_pat_subcycle, clock cycles are counted and a subcycle pulse is generated. This subcycle enables finer division of the synchronization pulse and is used for synchronizations with phase shifts. The configuration parameter wfg_pat_sync specifies the number of subcycle pulses during a sync period. The resulting sync frequency 10 | 11 | ![Formula](formula.png) 12 | 13 | can be determined using these two parameters. The outputs of the module are subcycle and sync pulses, as well as three optional status signals (active, start pulse, subcyle counter). -------------------------------------------------------------------------------- /design/wfg_core/data/wfg_core_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Core control register 3 | ,,EN,rw,cfg,0,0,1'b0,Core enable 4 | 4'h4,CFG,,,,,,,Core configuration register 5 | ,,SYNC,rw,cfg,7,0,0,"Sync pulse clock divider. 6 | Count clk until threshold is reached" 7 | ,,SUBCYCLE,rw,cfg,23,8,0,"Subcycle pulse clock divider. 8 | Count clk until threshold is reached" 9 | -------------------------------------------------------------------------------- /design/wfg_core/data/wfg_core_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CFG": { 4 | "address": "4'h4", 5 | "description": "Core configuration register", 6 | "entries": { 7 | "SUBCYCLE": { 8 | "LSB": "8", 9 | "MSB": "23", 10 | "access": "rw", 11 | "description": "Subcycle pulse clock divider.\nCount clk until threshold is reached", 12 | "hardware": "cfg", 13 | "reset": "0" 14 | }, 15 | "SYNC": { 16 | "LSB": "0", 17 | "MSB": "7", 18 | "access": "rw", 19 | "description": "Sync pulse clock divider.\nCount clk until threshold is reached", 20 | "hardware": "cfg", 21 | "reset": "0" 22 | } 23 | } 24 | }, 25 | "CTRL": { 26 | "address": "4'h0", 27 | "description": "Core control register", 28 | "entries": { 29 | "EN": { 30 | "LSB": "0", 31 | "MSB": "0", 32 | "access": "rw", 33 | "description": "Core enable", 34 | "hardware": "cfg", 35 | "reset": "1'b0" 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /design/wfg_core/formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_core/formula.png -------------------------------------------------------------------------------- /design/wfg_core/rtl/wfg_core.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_core ( 6 | input wire clk, // I; System clock 7 | input wire rst_n, // I; Active low reset 8 | input wire en_i, // I; Enable signal 9 | 10 | // Config 11 | input wire [ 7:0] wfg_sync_count_i, // I; Sync counter threshold 12 | input wire [15:0] wfg_subcycle_count_i, // I; Subcycle counter threshold 13 | 14 | // Signals 15 | output wire wfg_core_sync_o, // O; Sync signal 16 | output wire wfg_core_subcycle_o, // O; Subcycle signal 17 | output wire wfg_core_start_o, // O; Indicate start 18 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter 19 | output wire active_o // O; Active indication signal 20 | ); 21 | 22 | // ------------------------------------------------------------------------- 23 | // Definition 24 | // ------------------------------------------------------------------------- 25 | 26 | logic [15:0] subcycle_count; // L; counting variable 27 | logic [ 7:0] sync_count; // L; counting variable 28 | logic temp_subcycle; // L; Internal subcycle 29 | logic temp_sync; // L; Internal subcycle 30 | logic subcycle_dly; // L; Rising edge det subcycle 31 | logic sync_dly; // L; Rising edge det sync 32 | logic en_i_dly; // L; Rising edge det enable 33 | logic [ 7:0] subcycle_pls_cnt; // L; Counts Subcycles 34 | 35 | // ------------------------------------------------------------------------- 36 | // Implementation 37 | // ------------------------------------------------------------------------- 38 | 39 | always @(posedge clk, negedge rst_n) begin 40 | 41 | if (~rst_n) begin 42 | subcycle_count <= 16'd0; 43 | sync_count <= 8'd0; 44 | temp_subcycle <= 1'b0; 45 | temp_sync <= 1'b0; 46 | subcycle_pls_cnt <= 8'd0; 47 | end else begin 48 | 49 | if (en_i) begin 50 | 51 | if (subcycle_count > 0) begin 52 | temp_subcycle <= temp_subcycle; 53 | subcycle_count <= subcycle_count - 1; 54 | end else begin 55 | temp_subcycle <= ~temp_subcycle; 56 | subcycle_count <= (wfg_subcycle_count_i); 57 | 58 | if (sync_count > 0) begin 59 | temp_sync <= temp_sync; 60 | sync_count <= sync_count - 1; 61 | end else begin 62 | temp_sync <= ~temp_sync; 63 | sync_count <= (wfg_sync_count_i); 64 | end 65 | 66 | end 67 | 68 | if (wfg_core_subcycle_o) begin 69 | subcycle_pls_cnt <= subcycle_pls_cnt + 1; 70 | end 71 | 72 | end else begin 73 | subcycle_count <= 8'd0; 74 | sync_count <= 8'd0; 75 | temp_subcycle <= 1'b0; 76 | temp_sync <= 1'b0; 77 | subcycle_pls_cnt <= 8'd0; 78 | end 79 | 80 | subcycle_dly <= temp_subcycle; 81 | sync_dly <= temp_sync; 82 | en_i_dly <= en_i; 83 | 84 | if (wfg_core_sync_o) begin 85 | subcycle_pls_cnt <= 8'd0; 86 | end 87 | 88 | end 89 | 90 | end 91 | 92 | assign wfg_core_subcycle_o = temp_subcycle & ~subcycle_dly; 93 | assign wfg_core_sync_o = temp_sync & ~sync_dly; 94 | assign active_o = en_i; 95 | assign wfg_core_start_o = en_i & ~en_i_dly; 96 | assign wfg_core_subcycle_cnt_o = subcycle_pls_cnt; 97 | 98 | endmodule 99 | `default_nettype wire 100 | -------------------------------------------------------------------------------- /design/wfg_core/rtl/wfg_core_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_core_top #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output wbs_ack_o, 18 | output [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Core synchronisation interface 21 | output wire wfg_core_sync_o, // O; Sync signal 22 | output wire wfg_core_subcycle_o, // O; Subcycle signal 23 | output wire wfg_core_start_o, // O; Indicate start 24 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter 25 | output wire active_o // O; Active indication signal 26 | ); 27 | // Registers 28 | //marker_template_start 29 | //data: ../data/wfg_core_reg.json 30 | //template: wishbone/instantiate_top.template 31 | //marker_template_code 32 | 33 | logic [23: 8] cfg_subcycle_q; // CFG.SUBCYCLE register output 34 | logic [ 7: 0] cfg_sync_q; // CFG.SYNC register output 35 | logic ctrl_en_q; // CTRL.EN register output 36 | 37 | //marker_template_end 38 | 39 | wfg_core_wishbone_reg wfg_core_wishbone_reg ( 40 | .wb_clk_i (wb_clk_i), 41 | .wb_rst_i (wb_rst_i), 42 | .wbs_stb_i(wbs_stb_i), 43 | .wbs_cyc_i(wbs_cyc_i), 44 | .wbs_we_i (wbs_we_i), 45 | .wbs_sel_i(wbs_sel_i), 46 | .wbs_dat_i(wbs_dat_i), 47 | .wbs_adr_i(wbs_adr_i), 48 | .wbs_ack_o(wbs_ack_o), 49 | .wbs_dat_o(wbs_dat_o), 50 | 51 | //marker_template_start 52 | //data: ../data/wfg_core_reg.json 53 | //template: wishbone/assign_to_module.template 54 | //marker_template_code 55 | 56 | .cfg_subcycle_q_o(cfg_subcycle_q), // CFG.SUBCYCLE register output 57 | .cfg_sync_q_o (cfg_sync_q), // CFG.SYNC register output 58 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output 59 | 60 | //marker_template_end 61 | ); 62 | 63 | wfg_core wfg_core ( 64 | .clk (wb_clk_i), // clock signal 65 | .rst_n(!wb_rst_i), // reset signal 66 | 67 | // Control 68 | .en_i(ctrl_en_q), // I; Enable signal 69 | 70 | // Configuration 71 | .wfg_sync_count_i (cfg_sync_q), // I; Sync counter threshold 72 | .wfg_subcycle_count_i(cfg_subcycle_q), // I: Subcycle counter threshold 73 | 74 | // Output 75 | .wfg_core_sync_o (wfg_core_sync_o), // O; Sync signal 76 | .wfg_core_subcycle_o (wfg_core_subcycle_o), // O; Subcycle signal 77 | .wfg_core_start_o (wfg_core_start_o), // O; Indicate start 78 | .wfg_core_subcycle_cnt_o(wfg_core_subcycle_cnt_o), // O; Subcycle pulse counter 79 | .active_o (active_o) // O; Active indication signal 80 | ); 81 | 82 | endmodule 83 | `default_nettype wire 84 | -------------------------------------------------------------------------------- /design/wfg_core/rtl/wfg_core_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_core_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_core_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic [23:8] cfg_subcycle_q_o, // CFG.SUBCYCLE register output 27 | output logic [ 7:0] cfg_sync_q_o, // CFG.SYNC register output 28 | output logic ctrl_en_q_o // CTRL.EN register output 29 | 30 | //marker_template_end 31 | ); 32 | 33 | //marker_template_start 34 | //data: ../data/wfg_core_reg.json 35 | //template: wishbone/instantiate_registers.template 36 | //marker_template_code 37 | 38 | logic [23: 8] cfg_subcycle_ff; // CFG.SUBCYCLE FF 39 | logic [ 7: 0] cfg_sync_ff; // CFG.SYNC FF 40 | logic ctrl_en_ff; // CTRL.EN FF 41 | 42 | //marker_template_end 43 | 44 | // Wishbone write to slave 45 | always_ff @(posedge wb_clk_i) begin 46 | if (wb_rst_i) begin 47 | //marker_template_start 48 | //data: ../data/wfg_core_reg.json 49 | //template: wishbone/reset_registers.template 50 | //marker_template_code 51 | 52 | cfg_subcycle_ff <= 0; 53 | cfg_sync_ff <= 0; 54 | ctrl_en_ff <= 1'b0; 55 | 56 | //marker_template_end 57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 58 | case (wbs_adr_i) 59 | //marker_template_start 60 | //data: ../data/wfg_core_reg.json 61 | //template: wishbone/assign_to_registers.template 62 | //marker_template_code 63 | 64 | 4'h4: begin 65 | cfg_subcycle_ff <= wbs_dat_i[23:8]; 66 | cfg_sync_ff <= wbs_dat_i[7:0]; 67 | end 68 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 69 | 70 | //marker_template_end 71 | default: begin 72 | end 73 | endcase 74 | end 75 | end 76 | 77 | // Wishbone read from slave 78 | always_ff @(posedge wb_clk_i) begin 79 | if (wb_rst_i) begin 80 | wbs_dat_o <= '0; 81 | end else begin 82 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 83 | wbs_dat_o <= '0; // default value 84 | case (wbs_adr_i) 85 | //marker_template_start 86 | //data: ../data/wfg_core_reg.json 87 | //template: wishbone/assign_from_registers.template 88 | //marker_template_code 89 | 90 | 4'h4: begin 91 | wbs_dat_o[23:8] <= cfg_subcycle_ff; 92 | wbs_dat_o[7:0] <= cfg_sync_ff; 93 | end 94 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 95 | 96 | //marker_template_end 97 | default: wbs_dat_o <= 'X; 98 | endcase 99 | end 100 | end 101 | end 102 | 103 | // Acknowledgement 104 | always_ff @(posedge wb_clk_i) begin 105 | if (wb_rst_i) wbs_ack_o <= 1'b0; 106 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 107 | end 108 | 109 | //marker_template_start 110 | //data: ../data/wfg_core_reg.json 111 | //template: wishbone/assign_outputs.template 112 | //marker_template_code 113 | 114 | assign cfg_subcycle_q_o = cfg_subcycle_ff; 115 | assign cfg_sync_q_o = cfg_sync_ff; 116 | assign ctrl_en_q_o = ctrl_en_ff; 117 | 118 | //marker_template_end 119 | endmodule 120 | `default_nettype wire 121 | -------------------------------------------------------------------------------- /design/wfg_core/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_core_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_core 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_core/sim/view.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI 3 | [*] Fri May 13 06:43:30 2022 4 | [*] 5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/wfg_core_tb.vcd" 6 | [dumpfile_mtime] "Fri May 13 06:30:51 2022" 7 | [dumpfile_size] 1052480 8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/view.gtkw" 9 | [timestart] 0 10 | [size] 1776 1043 11 | [pos] -1 -1 12 | *-20.000000 810000 -1 -1 -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] wfg_core_tb. 14 | [treeopen] wfg_core_tb.wfg_core_top. 15 | [sst_width] 227 16 | [signals_width] 488 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 410 19 | @200 20 | -Wishbone 21 | @28 22 | wfg_core_tb.io_wbs_clk 23 | @22 24 | wfg_core_tb.io_wbs_adr[31:0] 25 | wfg_core_tb.io_wbs_datrd[31:0] 26 | wfg_core_tb.io_wbs_datwr[31:0] 27 | @28 28 | wfg_core_tb.io_wbs_ack 29 | wfg_core_tb.io_wbs_cyc 30 | wfg_core_tb.io_wbs_rst 31 | wfg_core_tb.io_wbs_stb 32 | wfg_core_tb.io_wbs_we 33 | @200 34 | -Core 35 | @28 36 | wfg_core_tb.wfg_pat_start_o 37 | wfg_core_tb.wfg_pat_subcycle_o 38 | wfg_core_tb.wfg_pat_sync_o 39 | @22 40 | wfg_core_tb.wfg_pat_subcycle_cnt_o[7:0] 41 | @28 42 | wfg_core_tb.active_o 43 | @200 44 | -Internals 45 | @28 46 | wfg_core_tb.wfg_core_top.wfg_core.active_o 47 | wfg_core_tb.wfg_core_top.wfg_core.clk 48 | wfg_core_tb.wfg_core_top.wfg_core.en_i 49 | wfg_core_tb.wfg_core_top.wfg_core.en_i_dly 50 | wfg_core_tb.wfg_core_top.wfg_core.rst_n 51 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_start_o 52 | @22 53 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_count_ff[15:0] 54 | @28 55 | wfg_core_tb.wfg_core_top.wfg_core.temp_subcycle_ff 56 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_dly 57 | @22 58 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_pls_cnt[7:0] 59 | wfg_core_tb.wfg_core_top.wfg_core.sync_count_ff[7:0] 60 | @28 61 | wfg_core_tb.wfg_core_top.wfg_core.temp_sync_ff 62 | wfg_core_tb.wfg_core_top.wfg_core.sync_dly 63 | @22 64 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_cnt_o[7:0] 65 | @28 66 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_o 67 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_sync_o 68 | @22 69 | wfg_core_tb.wfg_core_top.wfg_core.wfg_subcycle_count_i[15:0] 70 | wfg_core_tb.wfg_core_top.wfg_core.wfg_sync_count_i[7:0] 71 | [pattern_trace] 1 72 | [pattern_trace] 0 73 | -------------------------------------------------------------------------------- /design/wfg_core/testbench/test_wfg_core.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import random 5 | import cocotb 6 | from cocotb.clock import Clock 7 | from cocotb.regression import TestFactory 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles 9 | from cocotbext.wishbone.driver import WishboneMaster 10 | from cocotbext.wishbone.driver import WBOp 11 | 12 | CLK_PER_SYNC = 300 13 | SYSCLK = 100000000 14 | DATA_CNT = 10 15 | 16 | short_per = Timer(100, units="ns") 17 | long_time = Timer(100, units="us") 18 | 19 | async def set_register(dut, wbs, address, data): 20 | dut._log.info(f"Set register {address} : {data}") 21 | 22 | wbRes = await wbs.send_cycle([WBOp(address, data)]) 23 | 24 | rvalues = [wb.datrd for wb in wbRes] 25 | dut._log.info(f"Returned values : {rvalues}") 26 | 27 | async def configure(dut, wbs, en, sync_count, subcycle_count): 28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8)) 29 | await set_register(dut, wbs, 0x0, en) # Enable core 30 | 31 | @cocotb.coroutine 32 | async def core_test(dut, en, sync_count, subcycle_count): 33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}") 34 | 35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start()) 36 | 37 | dut._log.info("Initialize and reset model") 38 | 39 | # Start reset 40 | dut.io_wbs_rst.value = 1 41 | await Timer(100, units='ns') 42 | 43 | # Stop reset 44 | dut.io_wbs_rst.value = 0 45 | 46 | # Wishbone Master 47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 48 | width=32, # size of data bus 49 | timeout=10) # in clock cycle number 50 | 51 | # Setup core 52 | await configure(dut, wbs, en, sync_count, subcycle_count) 53 | 54 | #await short_per 55 | #await short_per 56 | 57 | sync_pulse_count = 0 58 | clk_count = 0 59 | 60 | await FallingEdge(dut.wfg_core_sync_o) 61 | 62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3): 63 | await ClockCycles(dut.io_wbs_clk, 1) 64 | clk_count += 1 65 | 66 | if dut.wfg_core_sync_o == 1: 67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2) 68 | break 69 | 70 | if (dut.wfg_core_subcycle_o == 1): 71 | sync_pulse_count += 1 72 | clk_count = 0 73 | 74 | length = 2 75 | 76 | sync_array = [] 77 | 78 | for i in range(length): 79 | n = random.randint(1,2**7) 80 | sync_array.append(n) 81 | 82 | subcycle_array = [] 83 | 84 | for i in range(length): 85 | n = random.randint(1,2**7) 86 | subcycle_array.append(n) 87 | 88 | 89 | factory = TestFactory(core_test) 90 | factory.add_option("en", [1]) 91 | factory.add_option("sync_count", sync_array) 92 | factory.add_option("subcycle_count", subcycle_array) 93 | 94 | factory.generate_tests() 95 | -------------------------------------------------------------------------------- /design/wfg_core/testbench/wfg_core_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_core_tb #( 13 | parameter int BUSW = 32 14 | ) ( 15 | // Wishbone interface signals 16 | input io_wbs_clk, 17 | input io_wbs_rst, 18 | input [(BUSW-1):0] io_wbs_adr, 19 | input [(BUSW-1):0] io_wbs_datwr, 20 | output [(BUSW-1):0] io_wbs_datrd, 21 | input io_wbs_we, 22 | input io_wbs_stb, 23 | output io_wbs_ack, 24 | input io_wbs_cyc, 25 | 26 | // Core synchronisation interface 27 | output wire wfg_core_sync_o, // O; Sync signal 28 | output wire wfg_core_subcycle_o, // O; Subcycle signal 29 | output wire wfg_core_start_o, // O; Indicate start 30 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter 31 | output wire active_o // O; Active indication signal 32 | ); 33 | 34 | wfg_core_top wfg_core_top ( 35 | .wb_clk_i (io_wbs_clk), 36 | .wb_rst_i (io_wbs_rst), 37 | .wbs_stb_i(io_wbs_stb), 38 | .wbs_cyc_i(io_wbs_cyc), 39 | .wbs_we_i (io_wbs_we), 40 | .wbs_sel_i(4'b1111), 41 | .wbs_dat_i(io_wbs_datwr), 42 | .wbs_adr_i(io_wbs_adr), 43 | .wbs_ack_o(io_wbs_ack), 44 | .wbs_dat_o(io_wbs_datrd), 45 | 46 | // Core synchronisation interface 47 | .wfg_core_sync_o(wfg_core_sync_o), 48 | .wfg_core_subcycle_o(wfg_core_subcycle_o), 49 | .wfg_core_start_o(wfg_core_start_o), 50 | .wfg_core_subcycle_cnt_o(wfg_core_subcycle_cnt_o), 51 | .active_o(active_o) 52 | ); 53 | 54 | // Dump waves 55 | `ifndef VERILATOR 56 | initial begin 57 | $dumpfile("wfg_core_tb.vcd"); 58 | $dumpvars(0, wfg_core_tb); 59 | end 60 | `endif 61 | 62 | endmodule 63 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/README.md: -------------------------------------------------------------------------------- 1 | # wfg_drive_pat 2 | 3 | The module wfg_drive_pat drives a pattern onto the number of channels specified by the parameter CHANNELS. CHANNELS can range from 1 to 32. 4 | 5 | For correct operation it needs to be connected to a wfg_core. 6 | The signals used are *pat_sync* and *pat_subcycle_cnt*. 7 | 8 | The input is received via an axi stream (32 bit) interface. One transaction is performed after each *pat_sync*. The lowest CHANNELS bits are used for transmission, higher bits are discarded. 9 | 10 | The fields *begin* and *end* must be set for the channel to work correctly; 11 | begin can be set to a value of `1` to `subcycles - 1`, while end must be set from `begin+1` to `subcycles`, where `subcycles` is to the number of subcycles at which the core puts out *pat_sync* and in the next cycle resets the count to 0. 12 | 13 | Patselect can take values 0-3. 14 | - 0 corresponds to *Return to zero (RZ)* 15 | - 1 corresponds to *Return to one (RO)* 16 | - 2 corresponds to *Non return to one (NRZ)* 17 | - 3 corresponds to *Return to complement (RC)* 18 | 19 | ## Testbench 20 | 21 | *WIP* 22 | 23 | The module wfg_drive_pat is verified using a testbench written in cocotb and python. 24 | 25 | It uses and extends standard components of cocotb. 26 | 27 | 28 | ### Testcase generation 29 | 30 | Testcases are generated with a `TestFactory`. 31 | It takes a list of values for each input port and creates a testcase for each of the combinations. 32 | These values can be fixed to test certain cases, or be set to `None`, in this case they will either be randomized or use a default value. 33 | 34 | Input data for the AXI stream is randomly generated for each testcase run. 35 | 36 | 37 | ### Output checking 38 | 39 | A `OutputMonitor` captures all output values at each clockcycle. 40 | A `Scoreboard` checks the equivalence of the received values to previously generated expected output. 41 | 42 | Expected output is generated at the time the input is randomized. For each clockcycle it is calculated what the corresponding output should be based on the input and the *pat_subcycle_cnt*. 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/data/wfg_drive_pat_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Control register for pattern unit 3 | ,,EN,rw,cfg,31,0,0,Parallel Interface Output enable 4 | 4'h4,CFG,,,,,,,Pattern configuration register 5 | ,,BEGIN,rw,cfg,7,0,0,"Select at which subcycle the output occurs, binary encoded" 6 | ,,END,rw,cfg,15,8,0,"Select at which subcycle the transition according to PATSEL occurs, binary encoded" 7 | ,,CORE_SEL,rw,cfg,16,16,1'b0,"0 : core 8 | 1 : subcore" 9 | 4'h8,PATSEL0,,,,,,,Low bits of PATSEL 10 | ,,LOW,rw,cfg,31,0,0,"Select Output Format 11 | 12 | b00: Return to zero (RZ) 13 | b01: Return to one (RO) 14 | b10: Non return to one (NRZ) 15 | B11: Return to complement (RC) 16 | " 17 | 4'hC,PATSEL1,,,,,,,High bits of PATSEL 18 | ,,HIGH,rw,cfg,31,0,0,"Select Output Format 19 | 20 | b00: Return to zero (RZ) 21 | b01: Return to one (RO) 22 | b10: Non return to one (NRZ) 23 | B11: Return to complement (RC) 24 | " 25 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/data/wfg_drive_pat_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CFG": { 4 | "address": "4'h4", 5 | "description": "Pattern configuration register", 6 | "entries": { 7 | "BEGIN": { 8 | "LSB": "0", 9 | "MSB": "7", 10 | "access": "rw", 11 | "description": "Select at which subcycle the output occurs, binary encoded", 12 | "hardware": "cfg", 13 | "reset": "0" 14 | }, 15 | "CORE_SEL": { 16 | "LSB": "16", 17 | "MSB": "16", 18 | "access": "rw", 19 | "description": "0 : core\n1 : subcore", 20 | "hardware": "cfg", 21 | "reset": "1'b0" 22 | }, 23 | "END": { 24 | "LSB": "8", 25 | "MSB": "15", 26 | "access": "rw", 27 | "description": "Select at which subcycle the transition according to PATSEL occurs, binary encoded", 28 | "hardware": "cfg", 29 | "reset": "0" 30 | } 31 | } 32 | }, 33 | "CTRL": { 34 | "address": "4'h0", 35 | "description": "Control register for pattern unit", 36 | "entries": { 37 | "EN": { 38 | "LSB": "0", 39 | "MSB": "31", 40 | "access": "rw", 41 | "description": "Parallel Interface Output enable", 42 | "hardware": "cfg", 43 | "reset": "0" 44 | } 45 | } 46 | }, 47 | "PATSEL0": { 48 | "address": "4'h8", 49 | "description": "Low bits of PATSEL", 50 | "entries": { 51 | "LOW": { 52 | "LSB": "0", 53 | "MSB": "31", 54 | "access": "rw", 55 | "description": "Select Output Format\n \n b00: Return to zero (RZ)\n b01: Return to one (RO)\n b10: Non return to one (NRZ)\n B11: Return to complement (RC)\n", 56 | "hardware": "cfg", 57 | "reset": "0" 58 | } 59 | } 60 | }, 61 | "PATSEL1": { 62 | "address": "4'hC", 63 | "description": "High bits of PATSEL", 64 | "entries": { 65 | "HIGH": { 66 | "LSB": "0", 67 | "MSB": "31", 68 | "access": "rw", 69 | "description": "Select Output Format\n \n b00: Return to zero (RZ)\n b01: Return to one (RO)\n b10: Non return to one (NRZ)\n B11: Return to complement (RC)\n", 70 | "hardware": "cfg", 71 | "reset": "0" 72 | } 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /design/wfg_drive_pat/rtl/wfg_drive_pat.sv: -------------------------------------------------------------------------------- 1 | // Copyright Message 2 | // -------------------------------------------------------------------------- 3 | // 4 | // CONFIDENTIAL and PROPRIETARY 5 | // COPYRIGHT (c) semify 2021 6 | // 7 | // All rights are reserved. Reproduction in whole or in part is 8 | // prohibited without the written consent of the copyright owner. 9 | // 10 | // ---------------------------------------------------------------------------- 11 | // Design Information 12 | // ---------------------------------------------------------------------------- 13 | // 14 | // Author: Erwin Peterlin 15 | // 16 | // Description : wfg_drive_pat 17 | // 18 | 19 | module wfg_drive_pat #( 20 | parameter int CHANNELS = 8, 21 | parameter int AXIS_WIDTH = 32 22 | ) ( 23 | input logic clk, // I; System clock 24 | input logic rst_n, // I; Active low reset 25 | 26 | // Core synchronisation interface 27 | input logic wfg_core_sync_i, // I; Start Pulse for one cycle 28 | input logic [7:0] wfg_core_subcycle_cnt_i, // I; Current Subcycle 29 | 30 | // Subcore synchronisation interface 31 | input logic wfg_subcore_sync_i, // I; Start Pulse for one cycle 32 | input logic [7:0] wfg_subcore_subcycle_cnt_i, // I; Current Subcycle 33 | 34 | 35 | input logic [CHANNELS-1:0] ctrl_en_q_i, // I; Enable signal 36 | input logic [(CHANNELS*2)-1:0] patsel_q_i, // I; Output pattern selector 37 | input logic [7:0] cfg_begin_q_i, // I; Selects at which subcycle the output begins 38 | input logic [7:0] cfg_end_q_i, // I; Selects at which subcycle the output ends 39 | input logic cfg_core_sel_q_i, // I; Core select 40 | 41 | // AXI streaming interface 42 | output wire wfg_axis_tready_o, // O; ready 43 | input wire wfg_axis_tvalid_i, // I; valid 44 | input wire wfg_axis_tlast_i, // I; last 45 | input wire [31:0] wfg_axis_tdata_i, // I; data 46 | 47 | output logic [CHANNELS-1:0] pat_dout_o, // O; output pins 48 | output logic [CHANNELS-1:0] pat_dout_en_o // O; output enabled 49 | ); 50 | 51 | // ------------------------------------------------------------------------- 52 | // Definition 53 | // ------------------------------------------------------------------------- 54 | 55 | assign pat_dout_en_o = ctrl_en_q_i; 56 | 57 | logic [AXIS_WIDTH-1:0] axis_data_ff; 58 | logic axis_ready_ff; 59 | assign wfg_axis_tready_o = axis_ready_ff; 60 | 61 | logic wfg_sync_i; 62 | logic [7:0] wfg_subcycle_cnt_i; 63 | 64 | // ------------------------------------------------------------------------- 65 | // Implementation 66 | // ------------------------------------------------------------------------- 67 | 68 | always_comb begin 69 | wfg_sync_i = 'x; 70 | wfg_subcycle_cnt_i = 'x; 71 | 72 | case (cfg_core_sel_q_i) 73 | 1'b0: begin 74 | wfg_sync_i = wfg_core_sync_i; 75 | wfg_subcycle_cnt_i = wfg_core_subcycle_cnt_i; 76 | end 77 | 1'b1: begin 78 | wfg_sync_i = wfg_subcore_sync_i; 79 | wfg_subcycle_cnt_i = wfg_subcore_subcycle_cnt_i; 80 | end 81 | endcase 82 | end 83 | 84 | genvar k; 85 | generate 86 | for (k = 0; k < CHANNELS; k++) begin : gen_channels 87 | wfg_drive_pat_channel drv ( 88 | .clk(clk), 89 | .rst_n(rst_n), 90 | .wfg_core_subcycle_cnt_i(wfg_subcycle_cnt_i), 91 | .patsel_q_i({patsel_q_i[k+32], patsel_q_i[k]}), 92 | .cfg_begin_q_i(cfg_begin_q_i), 93 | .cfg_end_q_i(cfg_end_q_i), 94 | .ctrl_en_q_i(ctrl_en_q_i[k]), 95 | .axis_data_ff(axis_data_ff[k]), 96 | .data_o(pat_dout_o[k]) 97 | ); 98 | end 99 | endgenerate 100 | 101 | // ------------------------------------------------------------------------- 102 | // ff 103 | // ------------------------------------------------------------------------- 104 | 105 | always_ff @(posedge clk or negedge rst_n) begin 106 | if (!rst_n) begin 107 | axis_data_ff <= '0; 108 | axis_ready_ff <= '0; 109 | end else begin 110 | 111 | if (wfg_sync_i) begin 112 | axis_ready_ff <= '1; 113 | 114 | if (wfg_axis_tvalid_i) begin 115 | axis_data_ff <= wfg_axis_tdata_i; 116 | end 117 | end else begin 118 | axis_ready_ff <= '0; 119 | end 120 | 121 | end 122 | end //always_ff 123 | endmodule 124 | 125 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/rtl/wfg_drive_pat_channel.sv: -------------------------------------------------------------------------------- 1 | // CONFIDENTIAL and PROPRIETARY 2 | // COPYRIGHT (c) semify 2021 3 | // 4 | // All rights are reserved. Reproduction in whole or in part is 5 | // prohibited without the written consent of the copyright owner. 6 | // 7 | // ---------------------------------------------------------------------------- 8 | // Design Information 9 | // ---------------------------------------------------------------------------- 10 | // 11 | // Author: Erwin Peterlin 12 | // 13 | // Description : wfg_drive_pat_channel 14 | // 15 | module wfg_drive_pat_channel ( 16 | input logic clk, // I; System clock 17 | input logic rst_n, // I; Active low reset 18 | 19 | input logic [7:0] wfg_core_subcycle_cnt_i, 20 | input logic [1:0] patsel_q_i, 21 | input logic [7:0] cfg_begin_q_i, 22 | input logic [7:0] cfg_end_q_i, 23 | input logic ctrl_en_q_i, 24 | input logic axis_data_ff, 25 | 26 | output logic data_o 27 | ); 28 | 29 | logic data_next, data_ff; 30 | assign data_o = data_ff; 31 | 32 | always_comb begin 33 | data_next = data_ff; 34 | 35 | if (wfg_core_subcycle_cnt_i == cfg_begin_q_i) begin 36 | data_next = axis_data_ff; 37 | end //if 38 | 39 | if (wfg_core_subcycle_cnt_i == cfg_end_q_i) begin 40 | case (patsel_q_i) 41 | 2'b00: begin 42 | data_next = '0; 43 | end //RZ 44 | 2'b01: begin 45 | data_next = '1; 46 | end //RO 47 | 2'b10: begin 48 | data_next = data_ff; 49 | end //NRZ 50 | 2'b11: begin 51 | if (data_ff == '0 || data_ff == '1) begin 52 | data_next = !axis_data_ff; 53 | end //if 54 | end //RC 55 | default: data_next = 'x; 56 | //default: $error("invalid pat_select"); 57 | endcase 58 | end //if 59 | 60 | if (!ctrl_en_q_i) begin 61 | data_next = '0; 62 | end //if 63 | end //always_comb 64 | 65 | 66 | always_ff @(posedge clk or negedge rst_n) begin 67 | if (!rst_n) begin 68 | data_ff <= '0; 69 | end else begin 70 | data_ff <= data_next; 71 | end 72 | end //always_ff 73 | endmodule 74 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/rtl/wfg_drive_pat_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_drive_pat_top #( 6 | parameter int BUSW = 32, 7 | parameter int AXIS_DATA_WIDTH = 32, 8 | parameter int CHANNELS = 32 9 | ) ( 10 | // Wishbone Slave ports 11 | input wb_clk_i, 12 | input wb_rst_i, 13 | input wbs_stb_i, 14 | input wbs_cyc_i, 15 | input wbs_we_i, 16 | input [(BUSW/8-1):0] wbs_sel_i, 17 | input [ (BUSW-1):0] wbs_dat_i, 18 | input [ (BUSW-1):0] wbs_adr_i, 19 | output wbs_ack_o, 20 | output [ (BUSW-1):0] wbs_dat_o, 21 | 22 | // Core synchronisation interface 23 | input wire wfg_core_sync_i, // I; sync pulse 24 | input logic [7:0] wfg_core_subcycle_cnt_i, // I; subcycle_cnt 25 | 26 | // Subcore synchronisation interface 27 | input logic wfg_subcore_sync_i, // I; sync pulse 28 | input logic [7:0] wfg_subcore_subcycle_cnt_i, // I; subcycle_cnt 29 | 30 | // AXI-Stream interface 31 | output wire wfg_axis_tready_o, 32 | input wire [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata_i, 33 | input wire wfg_axis_tlast_i, 34 | input wire wfg_axis_tvalid_i, 35 | 36 | output logic [CHANNELS-1:0] pat_dout_o, // O; output pins 37 | output logic [CHANNELS-1:0] pat_dout_en_o // O; output enabled 38 | ); 39 | // Registers 40 | //marker_template_start 41 | //data: ../data/wfg_drive_pat_reg.json 42 | //template: wishbone/instantiate_top.template 43 | //marker_template_code 44 | 45 | logic [ 7: 0] cfg_begin_q; // CFG.BEGIN register output 46 | logic cfg_core_sel_q; // CFG.CORE_SEL register output 47 | logic [15: 8] cfg_end_q; // CFG.END register output 48 | logic [31: 0] ctrl_en_q; // CTRL.EN register output 49 | logic [31: 0] patsel0_low_q; // PATSEL0.LOW register output 50 | logic [31: 0] patsel1_high_q; // PATSEL1.HIGH register output 51 | 52 | //marker_template_end 53 | 54 | wfg_drive_pat_wishbone_reg wfg_drive_pat_wishbone_reg ( 55 | .wb_clk_i (wb_clk_i), 56 | .wb_rst_i (wb_rst_i), 57 | .wbs_stb_i(wbs_stb_i), 58 | .wbs_cyc_i(wbs_cyc_i), 59 | .wbs_we_i (wbs_we_i), 60 | .wbs_sel_i(wbs_sel_i), 61 | .wbs_dat_i(wbs_dat_i), 62 | .wbs_adr_i(wbs_adr_i), 63 | .wbs_ack_o(wbs_ack_o), 64 | .wbs_dat_o(wbs_dat_o), 65 | 66 | //marker_template_start 67 | //data: ../data/wfg_drive_pat_reg.json 68 | //template: wishbone/assign_to_module.template 69 | //marker_template_code 70 | 71 | .cfg_begin_q_o (cfg_begin_q), // CFG.BEGIN register output 72 | .cfg_core_sel_q_o(cfg_core_sel_q), // CFG.CORE_SEL register output 73 | .cfg_end_q_o (cfg_end_q), // CFG.END register output 74 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output 75 | .patsel0_low_q_o (patsel0_low_q), // PATSEL0.LOW register output 76 | .patsel1_high_q_o(patsel1_high_q) // PATSEL1.HIGH register output 77 | 78 | //marker_template_end 79 | ); 80 | 81 | wfg_drive_pat #( 82 | .CHANNELS (CHANNELS), 83 | .AXIS_WIDTH(AXIS_DATA_WIDTH) 84 | ) wfg_drive_pat ( 85 | .clk (wb_clk_i), // clock signal 86 | .rst_n(!wb_rst_i), // reset signal 87 | 88 | // Core synchronisation interface 89 | .wfg_core_sync_i (wfg_core_sync_i), 90 | .wfg_core_subcycle_cnt_i(wfg_core_subcycle_cnt_i), 91 | 92 | // Subcore synchronisation interface 93 | .wfg_subcore_sync_i (wfg_subcore_sync_i), 94 | .wfg_subcore_subcycle_cnt_i(wfg_subcore_subcycle_cnt_i), 95 | 96 | // AXI streaming interface 97 | .wfg_axis_tready_o(wfg_axis_tready_o), // O; ready 98 | .wfg_axis_tvalid_i(wfg_axis_tvalid_i), // I; valid 99 | .wfg_axis_tlast_i (wfg_axis_tlast_i), // I; last 100 | .wfg_axis_tdata_i (wfg_axis_tdata_i), // I; data 101 | 102 | // Control 103 | .ctrl_en_q_i(ctrl_en_q), // I; pat enable 104 | 105 | // Configuration 106 | .cfg_begin_q_i (cfg_begin_q), 107 | .cfg_end_q_i (cfg_end_q), 108 | .patsel_q_i ({patsel1_high_q, patsel0_low_q}), 109 | .cfg_core_sel_q_i(cfg_core_sel_q), 110 | 111 | // Output 112 | .pat_dout_o (pat_dout_o), 113 | .pat_dout_en_o(pat_dout_en_o) 114 | ); 115 | 116 | endmodule 117 | `default_nettype wire 118 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/rtl/wfg_drive_pat_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_drive_pat_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_drive_pat_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic [ 7:0] cfg_begin_q_o, // CFG.BEGIN register output 27 | output logic cfg_core_sel_q_o, // CFG.CORE_SEL register output 28 | output logic [15:8] cfg_end_q_o, // CFG.END register output 29 | output logic [31:0] ctrl_en_q_o, // CTRL.EN register output 30 | output logic [31:0] patsel0_low_q_o, // PATSEL0.LOW register output 31 | output logic [31:0] patsel1_high_q_o // PATSEL1.HIGH register output 32 | 33 | //marker_template_end 34 | ); 35 | 36 | //marker_template_start 37 | //data: ../data/wfg_drive_pat_reg.json 38 | //template: wishbone/instantiate_registers.template 39 | //marker_template_code 40 | 41 | logic [ 7: 0] cfg_begin_ff; // CFG.BEGIN FF 42 | logic cfg_core_sel_ff; // CFG.CORE_SEL FF 43 | logic [15: 8] cfg_end_ff; // CFG.END FF 44 | logic [31: 0] ctrl_en_ff; // CTRL.EN FF 45 | logic [31: 0] patsel0_low_ff; // PATSEL0.LOW FF 46 | logic [31: 0] patsel1_high_ff; // PATSEL1.HIGH FF 47 | 48 | //marker_template_end 49 | 50 | // Wishbone write to slave 51 | always_ff @(posedge wb_clk_i) begin 52 | if (wb_rst_i) begin 53 | //marker_template_start 54 | //data: ../data/wfg_drive_pat_reg.json 55 | //template: wishbone/reset_registers.template 56 | //marker_template_code 57 | 58 | cfg_begin_ff <= 0; 59 | cfg_core_sel_ff <= 1'b0; 60 | cfg_end_ff <= 0; 61 | ctrl_en_ff <= 0; 62 | patsel0_low_ff <= 0; 63 | patsel1_high_ff <= 0; 64 | 65 | //marker_template_end 66 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 67 | case (wbs_adr_i) 68 | //marker_template_start 69 | //data: ../data/wfg_drive_pat_reg.json 70 | //template: wishbone/assign_to_registers.template 71 | //marker_template_code 72 | 73 | 4'h4: begin 74 | cfg_begin_ff <= wbs_dat_i[7:0]; 75 | cfg_core_sel_ff <= wbs_dat_i[16:16]; 76 | cfg_end_ff <= wbs_dat_i[15:8]; 77 | end 78 | 4'h0: ctrl_en_ff <= wbs_dat_i[31: 0]; 79 | 4'h8: patsel0_low_ff <= wbs_dat_i[31: 0]; 80 | 4'hC: patsel1_high_ff <= wbs_dat_i[31: 0]; 81 | 82 | //marker_template_end 83 | default: begin 84 | end 85 | endcase 86 | end 87 | end 88 | 89 | // Wishbone read from slave 90 | always_ff @(posedge wb_clk_i) begin 91 | if (wb_rst_i) begin 92 | wbs_dat_o <= '0; 93 | end else begin 94 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 95 | wbs_dat_o <= '0; // default value 96 | case (wbs_adr_i) 97 | //marker_template_start 98 | //data: ../data/wfg_drive_pat_reg.json 99 | //template: wishbone/assign_from_registers.template 100 | //marker_template_code 101 | 102 | 4'h4: begin 103 | wbs_dat_o[7:0] <= cfg_begin_ff; 104 | wbs_dat_o[16:16] <= cfg_core_sel_ff; 105 | wbs_dat_o[15:8] <= cfg_end_ff; 106 | end 107 | 4'h0: wbs_dat_o[31: 0] <= ctrl_en_ff; 108 | 4'h8: wbs_dat_o[31: 0] <= patsel0_low_ff; 109 | 4'hC: wbs_dat_o[31: 0] <= patsel1_high_ff; 110 | 111 | //marker_template_end 112 | default: wbs_dat_o <= 'X; 113 | endcase 114 | end 115 | end 116 | end 117 | 118 | // Acknowledgement 119 | always_ff @(posedge wb_clk_i) begin 120 | if (wb_rst_i) wbs_ack_o <= 1'b0; 121 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 122 | end 123 | 124 | //marker_template_start 125 | //data: ../data/wfg_drive_pat_reg.json 126 | //template: wishbone/assign_outputs.template 127 | //marker_template_code 128 | 129 | assign cfg_begin_q_o = cfg_begin_ff; 130 | assign cfg_core_sel_q_o = cfg_core_sel_ff; 131 | assign cfg_end_q_o = cfg_end_ff; 132 | assign ctrl_en_q_o = ctrl_en_ff; 133 | assign patsel0_low_q_o = patsel0_low_ff; 134 | assign patsel1_high_q_o = patsel1_high_ff; 135 | 136 | //marker_template_end 137 | endmodule 138 | `default_nettype wire 139 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_drive_pat_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_drive_pat 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_drive_pat/testbench/wfg_drive_pat_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_drive_pat_tb #( 13 | parameter int BUSW = 32, 14 | parameter int AXIS_DATA_WIDTH = 32, 15 | parameter int CHANNELS = 32 16 | ) ( 17 | // Wishbone interface signals 18 | input io_wbs_clk, 19 | input io_wbs_rst, 20 | input [(BUSW-1):0] io_wbs_adr, 21 | input [(BUSW-1):0] io_wbs_datwr, 22 | output [(BUSW-1):0] io_wbs_datrd, 23 | input io_wbs_we, 24 | input io_wbs_stb, 25 | output io_wbs_ack, 26 | input io_wbs_cyc, 27 | 28 | // Core synchronisation interface 29 | input logic wfg_core_sync_i, 30 | input logic [7:0] wfg_core_subcycle_cnt_i, 31 | 32 | // AXI-Stream interface 33 | output wire wfg_axis_tready, // O; ready 34 | input logic wfg_axis_tvalid, // I; valid 35 | input logic wfg_axis_tlast, // I; last 36 | input logic [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata, // I; data 37 | 38 | // pat IO interface 39 | output wire [CHANNELS-1:0] wfg_drive_pat_dout_o, // O; output pins 40 | output wire [CHANNELS-1:0] wfg_drive_pat_dout_en_o // O; output enabled 41 | ); 42 | 43 | wfg_drive_pat_top #( 44 | .BUSW(BUSW), 45 | .CHANNELS(CHANNELS), 46 | .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH) 47 | ) wfg_drive_pat_top ( 48 | .wb_clk_i (io_wbs_clk), 49 | .wb_rst_i (io_wbs_rst), 50 | .wbs_stb_i(io_wbs_stb), 51 | .wbs_cyc_i(io_wbs_cyc), 52 | .wbs_we_i (io_wbs_we), 53 | .wbs_sel_i(4'b1111), 54 | .wbs_dat_i(io_wbs_datwr), 55 | .wbs_adr_i(io_wbs_adr), 56 | .wbs_ack_o(io_wbs_ack), 57 | .wbs_dat_o(io_wbs_datrd), 58 | 59 | .wfg_core_sync_i(wfg_core_sync_i), 60 | .wfg_core_subcycle_cnt_i(wfg_core_subcycle_cnt_i), 61 | 62 | .wfg_axis_tready_o(wfg_axis_tready), 63 | .wfg_axis_tdata_i (wfg_axis_tdata), 64 | .wfg_axis_tlast_i (wfg_axis_tlast), 65 | .wfg_axis_tvalid_i(wfg_axis_tvalid), 66 | 67 | .pat_dout_o(wfg_drive_pat_dout_o), 68 | .pat_dout_en_o(wfg_drive_pat_dout_en_o) 69 | ); 70 | 71 | // Dump waves 72 | `ifndef VERILATOR 73 | initial begin 74 | $dumpfile("wfg_drive_pat_tb.vcd"); 75 | $dumpvars(0, wfg_drive_pat_tb); 76 | end 77 | `endif 78 | 79 | endmodule 80 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | *Copyright © 2021* [Semify EDA]( 4 | https://github.com/semify-eda) 5 | 6 | ## wfg_drive_spi documentation 7 | 8 | #### Overview 9 | 10 | The wfg_drive_spi module is a SPI master module used in the output stage of the waveform generator and is only operating in MOSI mode. The module is 11 | triggered by the wfg_core, each sync puls starts a new transmission. 12 | The input is received via an axi stream (32 bit) interface. 13 | The transmission is handled entirely within the module and all SPI signals are generated automatically and are configurable. 14 | 15 | #### Verification 16 | 17 | A system clock with frequency f = 100 MHz and an asynchronous wfg_sync signal are defined in the cocotb testbench. 18 | The Python function random.randint is used to generate a list of random input values. Via a cocotb AXI-Stream master the SPI module receives a new input 19 | at each sync pulse. The functionality is tested with a test factory that runs through all possible configuration parameters. 20 | The SPI module is connected to a cocotb SPI interface which decodes each successful transmission. 21 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/data/wfg_drive_spi_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Control register for SPI unit 3 | ,,EN,rw,cfg,0,0,1'b0,SPI enable 4 | 4'h4,CFG,,,,,,,SPI configuration register 5 | ,,CPOL,rw,cfg,0,0,1'b0,"Clock polarity 6 | 0: CK to 0 when idle 7 | 1: CK to 1 when idle" 8 | ,,LSBFIRST,rw,cfg,1,1,1'b0,"Frame format 9 | 0: MSB transmitted first 10 | 1: LSB transmitted first" 11 | ,,DFF,rw,cfg,3,2,2'b00,"Data frame format 12 | 00: 8bit 13 | 01: 16bit 14 | 10: 24bit 15 | 11: 32bit" 16 | ,,SSPOL,rw,cfg,4,4,1'b0,"Slave select polarity 17 | 0: Active low 18 | 1: Active high" 19 | ,,CORE_SEL,rw,cfg,5,5,1'b0,"0 : core 20 | 1 : subcore" 21 | 4'h8,CLKCFG,,,,,,,SPI clock configuration register 22 | ,,DIV,rw,cfg,7,0,0,"SPI Clock divider. 23 | Divider ratio is 2*DIV+1" 24 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/data/wfg_drive_spi_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CFG": { 4 | "address": "4'h4", 5 | "description": "SPI configuration register", 6 | "entries": { 7 | "CORE_SEL": { 8 | "LSB": "5", 9 | "MSB": "5", 10 | "access": "rw", 11 | "description": "0 : core\n1 : subcore", 12 | "hardware": "cfg", 13 | "reset": "1'b0" 14 | }, 15 | "CPOL": { 16 | "LSB": "0", 17 | "MSB": "0", 18 | "access": "rw", 19 | "description": "Clock polarity\n 0: CK to 0 when idle\n 1: CK to 1 when idle", 20 | "hardware": "cfg", 21 | "reset": "1'b0" 22 | }, 23 | "DFF": { 24 | "LSB": "2", 25 | "MSB": "3", 26 | "access": "rw", 27 | "description": "Data frame format\n 00: 8bit\n 01: 16bit\n 10: 24bit\n 11: 32bit", 28 | "hardware": "cfg", 29 | "reset": "2'b00" 30 | }, 31 | "LSBFIRST": { 32 | "LSB": "1", 33 | "MSB": "1", 34 | "access": "rw", 35 | "description": "Frame format\n 0: MSB transmitted first\n 1: LSB transmitted first", 36 | "hardware": "cfg", 37 | "reset": "1'b0" 38 | }, 39 | "SSPOL": { 40 | "LSB": "4", 41 | "MSB": "4", 42 | "access": "rw", 43 | "description": "Slave select polarity\n 0: Active low\n 1: Active high", 44 | "hardware": "cfg", 45 | "reset": "1'b0" 46 | } 47 | } 48 | }, 49 | "CLKCFG": { 50 | "address": "4'h8", 51 | "description": "SPI clock configuration register", 52 | "entries": { 53 | "DIV": { 54 | "LSB": "0", 55 | "MSB": "7", 56 | "access": "rw", 57 | "description": "SPI Clock divider.\nDivider ratio is 2*DIV+1", 58 | "hardware": "cfg", 59 | "reset": "0" 60 | } 61 | } 62 | }, 63 | "CTRL": { 64 | "address": "4'h0", 65 | "description": "Control register for SPI unit", 66 | "entries": { 67 | "EN": { 68 | "LSB": "0", 69 | "MSB": "0", 70 | "access": "rw", 71 | "description": "SPI enable", 72 | "hardware": "cfg", 73 | "reset": "1'b0" 74 | } 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /design/wfg_drive_spi/rtl/wfg_drive_spi_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_drive_spi_top #( 6 | parameter int BUSW = 32, 7 | parameter int AXIS_DATA_WIDTH = 32 8 | ) ( 9 | // Wishbone Slave ports 10 | input wb_clk_i, 11 | input wb_rst_i, 12 | input wbs_stb_i, 13 | input wbs_cyc_i, 14 | input wbs_we_i, 15 | input [(BUSW/8-1):0] wbs_sel_i, 16 | input [ (BUSW-1):0] wbs_dat_i, 17 | input [ (BUSW-1):0] wbs_adr_i, 18 | output wbs_ack_o, 19 | output [ (BUSW-1):0] wbs_dat_o, 20 | 21 | // Core synchronisation interface 22 | input wire wfg_core_sync_i, // I; core_sync pulse 23 | input wire wfg_core_subcycle_i, // I; subcycle_cnt 24 | 25 | // Subcore synchronisation interface 26 | input logic wfg_subcore_sync_i, // I; Sync pulse 27 | input logic wfg_subcore_subcycle_i, // I; Subcycle pulse 28 | 29 | // AXI-Stream interface 30 | output wire wfg_axis_tready_o, 31 | input wire [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata_i, 32 | input wire wfg_axis_tlast_i, 33 | input wire wfg_axis_tvalid_i, 34 | 35 | // SPI IO interface 36 | output wire wfg_drive_spi_sclk_o, // O; clock 37 | output wire wfg_drive_spi_cs_no, // O; chip select 38 | output wire wfg_drive_spi_sdo_o // O; data out 39 | ); 40 | // Registers 41 | //marker_template_start 42 | //data: ../data/wfg_drive_spi_reg.json 43 | //template: wishbone/instantiate_top.template 44 | //marker_template_code 45 | 46 | logic cfg_core_sel_q; // CFG.CORE_SEL register output 47 | logic cfg_cpol_q; // CFG.CPOL register output 48 | logic [ 3: 2] cfg_dff_q; // CFG.DFF register output 49 | logic cfg_lsbfirst_q; // CFG.LSBFIRST register output 50 | logic cfg_sspol_q; // CFG.SSPOL register output 51 | logic [ 7: 0] clkcfg_div_q; // CLKCFG.DIV register output 52 | logic ctrl_en_q; // CTRL.EN register output 53 | 54 | //marker_template_end 55 | 56 | wfg_drive_spi_wishbone_reg wfg_drive_spi_wishbone_reg ( 57 | .wb_clk_i (wb_clk_i), 58 | .wb_rst_i (wb_rst_i), 59 | .wbs_stb_i(wbs_stb_i), 60 | .wbs_cyc_i(wbs_cyc_i), 61 | .wbs_we_i (wbs_we_i), 62 | .wbs_sel_i(wbs_sel_i), 63 | .wbs_dat_i(wbs_dat_i), 64 | .wbs_adr_i(wbs_adr_i), 65 | .wbs_ack_o(wbs_ack_o), 66 | .wbs_dat_o(wbs_dat_o), 67 | 68 | //marker_template_start 69 | //data: ../data/wfg_drive_spi_reg.json 70 | //template: wishbone/assign_to_module.template 71 | //marker_template_code 72 | 73 | .cfg_core_sel_q_o(cfg_core_sel_q), // CFG.CORE_SEL register output 74 | .cfg_cpol_q_o (cfg_cpol_q), // CFG.CPOL register output 75 | .cfg_dff_q_o (cfg_dff_q), // CFG.DFF register output 76 | .cfg_lsbfirst_q_o(cfg_lsbfirst_q), // CFG.LSBFIRST register output 77 | .cfg_sspol_q_o (cfg_sspol_q), // CFG.SSPOL register output 78 | .clkcfg_div_q_o (clkcfg_div_q), // CLKCFG.DIV register output 79 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output 80 | 81 | //marker_template_end 82 | ); 83 | 84 | wfg_drive_spi wfg_drive_spi ( 85 | .clk (wb_clk_i), // clock signal 86 | .rst_n(!wb_rst_i), // reset signal 87 | 88 | // Core synchronisation interface 89 | .wfg_core_sync_i (wfg_core_sync_i), 90 | .wfg_core_subcycle_i(wfg_core_subcycle_i), 91 | 92 | // Subcore synchronisation interface 93 | .wfg_subcore_sync_i (wfg_subcore_sync_i), 94 | .wfg_subcore_subcycle_i(wfg_subcore_subcycle_i), 95 | 96 | // AXI streaming interface 97 | .wfg_axis_tready_o(wfg_axis_tready_o), // O; ready 98 | .wfg_axis_tvalid_i(wfg_axis_tvalid_i), // I; valid 99 | .wfg_axis_tlast_i (wfg_axis_tlast_i), // I; last 100 | .wfg_axis_tdata_i (wfg_axis_tdata_i), // I; data 101 | 102 | // Control 103 | .ctrl_en_q_i(ctrl_en_q), // I; SPI enable 104 | 105 | // Configuration 106 | .clkcfg_div_q_i (clkcfg_div_q), // I: clock divider 107 | .cfg_cpol_q_i (cfg_cpol_q), // I; Clock polarity 108 | .cfg_lsbfirst_q_i(cfg_lsbfirst_q), // I; Frame format 109 | .cfg_dff_q_i (cfg_dff_q), // I; Data frame format 110 | .cfg_sspol_q_i (cfg_sspol_q), // I; Slave select polarity 111 | .cfg_core_sel_q_i(cfg_core_sel_q), // I; Core select 112 | 113 | // SPI IO interface 114 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o), // O; clock 115 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no), // O; chip select 116 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o) // O; data out 117 | ); 118 | 119 | endmodule 120 | `default_nettype wire 121 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/rtl/wfg_drive_spi_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_drive_spi_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_drive_spi_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic cfg_core_sel_q_o, // CFG.CORE_SEL register output 27 | output logic cfg_cpol_q_o, // CFG.CPOL register output 28 | output logic [3:2] cfg_dff_q_o, // CFG.DFF register output 29 | output logic cfg_lsbfirst_q_o, // CFG.LSBFIRST register output 30 | output logic cfg_sspol_q_o, // CFG.SSPOL register output 31 | output logic [7:0] clkcfg_div_q_o, // CLKCFG.DIV register output 32 | output logic ctrl_en_q_o // CTRL.EN register output 33 | 34 | //marker_template_end 35 | ); 36 | 37 | //marker_template_start 38 | //data: ../data/wfg_drive_spi_reg.json 39 | //template: wishbone/instantiate_registers.template 40 | //marker_template_code 41 | 42 | logic cfg_core_sel_ff; // CFG.CORE_SEL FF 43 | logic cfg_cpol_ff; // CFG.CPOL FF 44 | logic [ 3: 2] cfg_dff_ff; // CFG.DFF FF 45 | logic cfg_lsbfirst_ff; // CFG.LSBFIRST FF 46 | logic cfg_sspol_ff; // CFG.SSPOL FF 47 | logic [ 7: 0] clkcfg_div_ff; // CLKCFG.DIV FF 48 | logic ctrl_en_ff; // CTRL.EN FF 49 | 50 | //marker_template_end 51 | 52 | // Wishbone write to slave 53 | always_ff @(posedge wb_clk_i) begin 54 | if (wb_rst_i) begin 55 | //marker_template_start 56 | //data: ../data/wfg_drive_spi_reg.json 57 | //template: wishbone/reset_registers.template 58 | //marker_template_code 59 | 60 | cfg_core_sel_ff <= 1'b0; 61 | cfg_cpol_ff <= 1'b0; 62 | cfg_dff_ff <= 2'b00; 63 | cfg_lsbfirst_ff <= 1'b0; 64 | cfg_sspol_ff <= 1'b0; 65 | clkcfg_div_ff <= 0; 66 | ctrl_en_ff <= 1'b0; 67 | 68 | //marker_template_end 69 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 70 | case (wbs_adr_i) 71 | //marker_template_start 72 | //data: ../data/wfg_drive_spi_reg.json 73 | //template: wishbone/assign_to_registers.template 74 | //marker_template_code 75 | 76 | 4'h4: begin 77 | cfg_core_sel_ff <= wbs_dat_i[5:5]; 78 | cfg_cpol_ff <= wbs_dat_i[0:0]; 79 | cfg_dff_ff <= wbs_dat_i[3:2]; 80 | cfg_lsbfirst_ff <= wbs_dat_i[1:1]; 81 | cfg_sspol_ff <= wbs_dat_i[4:4]; 82 | end 83 | 4'h8: clkcfg_div_ff <= wbs_dat_i[ 7: 0]; 84 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 85 | 86 | //marker_template_end 87 | default: begin 88 | end 89 | endcase 90 | end 91 | end 92 | 93 | // Wishbone read from slave 94 | always_ff @(posedge wb_clk_i) begin 95 | if (wb_rst_i) begin 96 | wbs_dat_o <= '0; 97 | end else begin 98 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 99 | wbs_dat_o <= '0; // default value 100 | case (wbs_adr_i) 101 | //marker_template_start 102 | //data: ../data/wfg_drive_spi_reg.json 103 | //template: wishbone/assign_from_registers.template 104 | //marker_template_code 105 | 106 | 4'h4: begin 107 | wbs_dat_o[5:5] <= cfg_core_sel_ff; 108 | wbs_dat_o[0:0] <= cfg_cpol_ff; 109 | wbs_dat_o[3:2] <= cfg_dff_ff; 110 | wbs_dat_o[1:1] <= cfg_lsbfirst_ff; 111 | wbs_dat_o[4:4] <= cfg_sspol_ff; 112 | end 113 | 4'h8: wbs_dat_o[ 7: 0] <= clkcfg_div_ff; 114 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 115 | 116 | //marker_template_end 117 | default: wbs_dat_o <= 'X; 118 | endcase 119 | end 120 | end 121 | end 122 | 123 | // Acknowledgement 124 | always_ff @(posedge wb_clk_i) begin 125 | if (wb_rst_i) wbs_ack_o <= 1'b0; 126 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 127 | end 128 | 129 | //marker_template_start 130 | //data: ../data/wfg_drive_spi_reg.json 131 | //template: wishbone/assign_outputs.template 132 | //marker_template_code 133 | 134 | assign cfg_core_sel_q_o = cfg_core_sel_ff; 135 | assign cfg_cpol_q_o = cfg_cpol_ff; 136 | assign cfg_dff_q_o = cfg_dff_ff; 137 | assign cfg_lsbfirst_q_o = cfg_lsbfirst_ff; 138 | assign cfg_sspol_q_o = cfg_sspol_ff; 139 | assign clkcfg_div_q_o = clkcfg_div_ff; 140 | assign ctrl_en_q_o = ctrl_en_ff; 141 | 142 | //marker_template_end 143 | endmodule 144 | `default_nettype wire 145 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_drive_spi_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_drive_spi 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/sim/view.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI 3 | [*] Wed May 11 08:07:10 2022 4 | [*] 5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/wfg_drive_spi_tb.vcd" 6 | [dumpfile_mtime] "Wed May 11 08:06:37 2022" 7 | [dumpfile_size] 99622 8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/view.gtkw" 9 | [timestart] 0 10 | [size] 1920 1011 11 | [pos] -1 -1 12 | *-23.000000 3060000 -1 -1 -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] wfg_drive_spi_tb. 14 | [treeopen] wfg_drive_spi_tb.wfg_drive_spi_top. 15 | [sst_width] 227 16 | [signals_width] 345 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 295 19 | @200 20 | -Wishbone 21 | @28 22 | wfg_drive_spi_tb.io_wbs_clk 23 | @22 24 | wfg_drive_spi_tb.io_wbs_adr[31:0] 25 | wfg_drive_spi_tb.io_wbs_datrd[31:0] 26 | wfg_drive_spi_tb.io_wbs_datwr[31:0] 27 | @28 28 | wfg_drive_spi_tb.io_wbs_ack 29 | wfg_drive_spi_tb.io_wbs_cyc 30 | wfg_drive_spi_tb.io_wbs_rst 31 | wfg_drive_spi_tb.io_wbs_stb 32 | wfg_drive_spi_tb.io_wbs_we 33 | @200 34 | -AXI 35 | @22 36 | wfg_drive_spi_tb.wfg_drive_spi_axis_tdata[31:0] 37 | @28 38 | wfg_drive_spi_tb.wfg_drive_spi_axis_tlast 39 | wfg_drive_spi_tb.wfg_drive_spi_axis_tready 40 | wfg_drive_spi_tb.wfg_drive_spi_axis_tvalid 41 | @200 42 | -SPI 43 | @29 44 | wfg_drive_spi_tb.wfg_drive_spi_sclk_o 45 | @28 46 | wfg_drive_spi_tb.wfg_drive_spi_cs_no 47 | wfg_drive_spi_tb.wfg_drive_spi_sdo_en_o 48 | wfg_drive_spi_tb.wfg_drive_spi_sdo_o 49 | wfg_drive_spi_tb.wfg_pat_sync_i 50 | wfg_drive_spi_tb.wgf_pat_subcycle_i 51 | @200 52 | -Register 53 | @28 54 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpha_ff 55 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpol_ff 56 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_dff_ff[5:4] 57 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_lsbfirst_ff 58 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_mstr_ff 59 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_oectrl_ff[11:10] 60 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_ssctrl_ff 61 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_sspol_ff 62 | @22 63 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.clkcfg_div_ff[7:0] 64 | @28 65 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.ctrl_en_ff 66 | @22 67 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_peripheral_type_ff[7:0] 68 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_version_ff[15:8] 69 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.reginfo_date_ff[17:0] 70 | @28 71 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.test_lpen_ff 72 | [pattern_trace] 1 73 | [pattern_trace] 0 74 | -------------------------------------------------------------------------------- /design/wfg_drive_spi/testbench/wfg_drive_spi_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_drive_spi_tb #( 13 | parameter int BUSW = 32, 14 | parameter int AXIS_DATA_WIDTH = 32 15 | ) ( 16 | // Wishbone interface signals 17 | input io_wbs_clk, 18 | input io_wbs_rst, 19 | input [(BUSW-1):0] io_wbs_adr, 20 | input [(BUSW-1):0] io_wbs_datwr, 21 | output [(BUSW-1):0] io_wbs_datrd, 22 | input io_wbs_we, 23 | input io_wbs_stb, 24 | output io_wbs_ack, 25 | input io_wbs_cyc, 26 | 27 | // Bus master A interface 28 | input logic wfg_core_sync_i, // I; Single cycle pulse for write access 29 | input logic wgf_core_subcycle_i, // I; Single cycle pulse for read access 30 | 31 | // AXI-Stream interface 32 | output wire wfg_axis_tready, // O; ready 33 | input logic wfg_axis_tvalid, // I; valid 34 | input logic wfg_axis_tlast, // I; last 35 | input logic [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata, // I; data 36 | 37 | // SPI IO interface 38 | output wire wfg_drive_spi_sclk_o, // O; clock 39 | output wire wfg_drive_spi_cs_no, // O; chip select 40 | output wire wfg_drive_spi_sdo_o, // O; data out 41 | input wire wfg_drive_spi_sdi_i // I; data in, dummy signal for cocotb 42 | ); 43 | 44 | wfg_drive_spi_top wfg_drive_spi_top ( 45 | .wb_clk_i (io_wbs_clk), 46 | .wb_rst_i (io_wbs_rst), 47 | .wbs_stb_i(io_wbs_stb), 48 | .wbs_cyc_i(io_wbs_cyc), 49 | .wbs_we_i (io_wbs_we), 50 | .wbs_sel_i(4'b1111), 51 | .wbs_dat_i(io_wbs_datwr), 52 | .wbs_adr_i(io_wbs_adr), 53 | .wbs_ack_o(io_wbs_ack), 54 | .wbs_dat_o(io_wbs_datrd), 55 | 56 | .wfg_core_sync_i(wfg_core_sync_i), 57 | .wfg_core_subcycle_i(wgf_core_subcycle_i), 58 | 59 | .wfg_axis_tready_o(wfg_axis_tready), 60 | .wfg_axis_tdata_i (wfg_axis_tdata), 61 | .wfg_axis_tlast_i (wfg_axis_tlast), 62 | .wfg_axis_tvalid_i(wfg_axis_tvalid), 63 | 64 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o), 65 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no), 66 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o) 67 | ); 68 | 69 | // Dump waves 70 | `ifndef VERILATOR 71 | initial begin 72 | $dumpfile("wfg_drive_spi_tb.vcd"); 73 | $dumpvars(0, wfg_drive_spi_tb); 74 | end 75 | `endif 76 | 77 | endmodule 78 | -------------------------------------------------------------------------------- /design/wfg_interconnect/data/wfg_interconnect_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Control register for interconnect 3 | ,,EN,rw,cfg,0,0,1'b0,Interconnect enable 4 | 4'h4,DRIVER0,,,,,,,Driver configuration register 5 | ,,SELECT,rw,cfg,1,0,0,"Select 6 | 0 – wfg_stim_sine 7 | 1 – wfg_stim_mem" 8 | 4'h8,DRIVER1,,,,,,,Driver configuration register 9 | ,,SELECT,rw,cfg,1,0,0,"Select 10 | 0 – wfg_stim_sine 11 | 1 – wfg_stim_mem" 12 | -------------------------------------------------------------------------------- /design/wfg_interconnect/data/wfg_interconnect_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CTRL": { 4 | "address": "4'h0", 5 | "description": "Control register for interconnect", 6 | "entries": { 7 | "EN": { 8 | "LSB": "0", 9 | "MSB": "0", 10 | "access": "rw", 11 | "description": "Interconnect enable", 12 | "hardware": "cfg", 13 | "reset": "1'b0" 14 | } 15 | } 16 | }, 17 | "DRIVER0": { 18 | "address": "4'h4", 19 | "description": "Driver configuration register", 20 | "entries": { 21 | "SELECT": { 22 | "LSB": "0", 23 | "MSB": "1", 24 | "access": "rw", 25 | "description": "Select\n0 \u2013 wfg_stim_sine\n1 \u2013 wfg_stim_mem", 26 | "hardware": "cfg", 27 | "reset": "0" 28 | } 29 | } 30 | }, 31 | "DRIVER1": { 32 | "address": "4'h8", 33 | "description": "Driver configuration register", 34 | "entries": { 35 | "SELECT": { 36 | "LSB": "0", 37 | "MSB": "1", 38 | "access": "rw", 39 | "description": "Select\n0 \u2013 wfg_stim_sine\n1 \u2013 wfg_stim_mem", 40 | "hardware": "cfg", 41 | "reset": "0" 42 | } 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /design/wfg_interconnect/rtl/wfg_interconnect.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | 6 | `ifndef WFG_INTERCONNECT_PKG 7 | `define WFG_INTERCONNECT_PKG 8 | typedef struct packed { 9 | logic wfg_axis_tvalid; 10 | logic [31:0] wfg_axis_tdata; 11 | } axis_t; 12 | `endif 13 | 14 | module wfg_interconnect #( 15 | parameter int AXIS_DATA_WIDTH = 32 16 | ) ( 17 | input logic clk, // I; System clock 18 | input logic rst_n, // I; active low reset 19 | 20 | // Control 21 | input logic ctrl_en_q_i, 22 | 23 | // Configuration 24 | input logic [1:0] driver0_select_q_i, 25 | input logic [1:0] driver1_select_q_i, 26 | 27 | // Stimuli 28 | input axis_t stimulus_0, 29 | input axis_t stimulus_1, 30 | 31 | output logic wfg_axis_tready_stimulus_0, 32 | output logic wfg_axis_tready_stimulus_1, 33 | 34 | // Driver 35 | output axis_t driver_0, 36 | output axis_t driver_1, 37 | 38 | input wfg_axis_tready_driver_0, 39 | input wfg_axis_tready_driver_1 40 | ); 41 | 42 | // Driver 0 43 | always_comb begin 44 | case (driver0_select_q_i) 45 | 2'b00: driver_0 = stimulus_0; 46 | 2'b01: driver_0 = stimulus_1; 47 | 2'b10: driver_0 = '0; 48 | 2'b11: driver_0 = '0; 49 | default: driver_0 = 'x; 50 | endcase 51 | end 52 | 53 | // Driver 1 54 | always_comb begin 55 | case (driver1_select_q_i) 56 | 2'b00: driver_1 = stimulus_0; 57 | 2'b01: driver_1 = stimulus_1; 58 | 2'b10: driver_1 = '0; 59 | 2'b11: driver_1 = '0; 60 | default: driver_1 = 'x; 61 | endcase 62 | end 63 | 64 | // Stimulus 0 65 | always_comb begin 66 | if (driver0_select_q_i == 2'b00 && driver1_select_q_i == 2'b00) begin 67 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_0 && wfg_axis_tready_driver_1; 68 | end else if (driver0_select_q_i == 2'b00) begin 69 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_0; 70 | end else if (driver1_select_q_i == 2'b00) begin 71 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_1; 72 | end else begin 73 | wfg_axis_tready_stimulus_0 = '0; 74 | end 75 | end 76 | 77 | // Stimulus 1 78 | always_comb begin 79 | if (driver0_select_q_i == 2'b01 && driver1_select_q_i == 2'b01) begin 80 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_0 && wfg_axis_tready_driver_1; 81 | end else if (driver0_select_q_i == 2'b01) begin 82 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_0; 83 | end else if (driver1_select_q_i == 2'b01) begin 84 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_1; 85 | end else begin 86 | wfg_axis_tready_stimulus_1 = '0; 87 | end 88 | end 89 | 90 | endmodule 91 | `default_nettype wire 92 | -------------------------------------------------------------------------------- /design/wfg_interconnect/rtl/wfg_interconnect_pkg.svh: -------------------------------------------------------------------------------- 1 | `ifndef WFG_INTERCONNECT_PKG 2 | `define WFG_INTERCONNECT_PKG 3 | 4 | //package wfg_interconnect_pkg; 5 | 6 | /*interface stimulus_if (input test); 7 | logic wfg_axis_tready; 8 | logic wfg_axis_tvalid; 9 | logic [31:0] wfg_axis_tdata; 10 | endinterface 11 | 12 | interface driver_if (input test); 13 | logic wfg_axis_tready; 14 | logic wfg_axis_tvalid; 15 | logic [31:0] wfg_axis_tdata; 16 | endinterface*/ 17 | 18 | typedef struct packed 19 | { 20 | logic wfg_axis_tvalid; 21 | logic [31:0] wfg_axis_tdata; 22 | } stimulus_t; 23 | 24 | typedef struct packed 25 | { 26 | logic wfg_pat_sync; 27 | logic wfg_pat_subcycle; 28 | logic [7:0] wfg_pat_subcycle_cnt; 29 | 30 | logic [31:0] wfg_axis_tdata; 31 | logic wfg_axis_tvalid; 32 | } driver_t; 33 | 34 | //endpackage 35 | 36 | `endif 37 | -------------------------------------------------------------------------------- /design/wfg_interconnect/rtl/wfg_interconnect_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | 6 | `ifndef WFG_INTERCONNECT_PKG 7 | `define WFG_INTERCONNECT_PKG 8 | typedef struct packed { 9 | logic wfg_axis_tvalid; 10 | logic [31:0] wfg_axis_tdata; 11 | } axis_t; 12 | `endif 13 | 14 | module wfg_interconnect_top #( 15 | parameter int BUSW = 32, 16 | parameter int AXIS_DATA_WIDTH = 32 17 | ) ( 18 | // Wishbone Slave ports 19 | input wb_clk_i, 20 | input wb_rst_i, 21 | input wbs_stb_i, 22 | input wbs_cyc_i, 23 | input wbs_we_i, 24 | input [(BUSW/8-1):0] wbs_sel_i, 25 | input [ (BUSW-1):0] wbs_dat_i, 26 | input [ (BUSW-1):0] wbs_adr_i, 27 | output wbs_ack_o, 28 | output [ (BUSW-1):0] wbs_dat_o, 29 | 30 | // Core synchronisation interface 31 | input wire wfg_pat_sync_i, // I; pat_sync pulse 32 | input wire wfg_pat_subcycle_i, // I; subcycle_cnt 33 | 34 | // Stimuli 35 | input axis_t stimulus_0, 36 | input axis_t stimulus_1, 37 | 38 | output wfg_axis_tready_stimulus_0, 39 | output wfg_axis_tready_stimulus_1, 40 | 41 | // Driver 42 | output axis_t driver_0, 43 | output axis_t driver_1, 44 | 45 | input wfg_axis_tready_driver_0, 46 | input wfg_axis_tready_driver_1 47 | ); 48 | // Registers 49 | //marker_template_start 50 | //data: ../data/wfg_interconnect_reg.json 51 | //template: wishbone/instantiate_top.template 52 | //marker_template_code 53 | 54 | logic ctrl_en_q; // CTRL.EN register output 55 | logic [ 1: 0] driver0_select_q; // DRIVER0.SELECT register output 56 | logic [ 1: 0] driver1_select_q; // DRIVER1.SELECT register output 57 | 58 | //marker_template_end 59 | 60 | wfg_interconnect_wishbone_reg wfg_interconnect_wishbone_reg ( 61 | .wb_clk_i (wb_clk_i), 62 | .wb_rst_i (wb_rst_i), 63 | .wbs_stb_i(wbs_stb_i), 64 | .wbs_cyc_i(wbs_cyc_i), 65 | .wbs_we_i (wbs_we_i), 66 | .wbs_sel_i(wbs_sel_i), 67 | .wbs_dat_i(wbs_dat_i), 68 | .wbs_adr_i(wbs_adr_i), 69 | .wbs_ack_o(wbs_ack_o), 70 | .wbs_dat_o(wbs_dat_o), 71 | 72 | //marker_template_start 73 | //data: ../data/wfg_interconnect_reg.json 74 | //template: wishbone/assign_to_module.template 75 | //marker_template_code 76 | 77 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output 78 | .driver0_select_q_o(driver0_select_q), // DRIVER0.SELECT register output 79 | .driver1_select_q_o(driver1_select_q) // DRIVER1.SELECT register output 80 | 81 | //marker_template_end 82 | ); 83 | 84 | wfg_interconnect wfg_interconnect ( 85 | .clk (wb_clk_i), // clock signal 86 | .rst_n(!wb_rst_i), // reset signal 87 | 88 | // Control 89 | .ctrl_en_q_i(ctrl_en_q), 90 | 91 | // Configuration 92 | .driver0_select_q_i(driver0_select_q), 93 | .driver1_select_q_i(driver1_select_q), 94 | 95 | .stimulus_0, 96 | .stimulus_1, 97 | 98 | .wfg_axis_tready_stimulus_0, 99 | .wfg_axis_tready_stimulus_1, 100 | 101 | .driver_0, 102 | .driver_1, 103 | 104 | .wfg_axis_tready_driver_0, 105 | .wfg_axis_tready_driver_1 106 | ); 107 | 108 | endmodule 109 | `default_nettype wire 110 | -------------------------------------------------------------------------------- /design/wfg_interconnect/rtl/wfg_interconnect_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_interconnect_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_interconnect_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic ctrl_en_q_o, // CTRL.EN register output 27 | output logic [1:0] driver0_select_q_o, // DRIVER0.SELECT register output 28 | output logic [1:0] driver1_select_q_o // DRIVER1.SELECT register output 29 | 30 | //marker_template_end 31 | ); 32 | 33 | //marker_template_start 34 | //data: ../data/wfg_interconnect_reg.json 35 | //template: wishbone/instantiate_registers.template 36 | //marker_template_code 37 | 38 | logic ctrl_en_ff; // CTRL.EN FF 39 | logic [ 1: 0] driver0_select_ff; // DRIVER0.SELECT FF 40 | logic [ 1: 0] driver1_select_ff; // DRIVER1.SELECT FF 41 | 42 | //marker_template_end 43 | 44 | // Wishbone write to slave 45 | always_ff @(posedge wb_clk_i) begin 46 | if (wb_rst_i) begin 47 | //marker_template_start 48 | //data: ../data/wfg_interconnect_reg.json 49 | //template: wishbone/reset_registers.template 50 | //marker_template_code 51 | 52 | ctrl_en_ff <= 1'b0; 53 | driver0_select_ff <= 0; 54 | driver1_select_ff <= 0; 55 | 56 | //marker_template_end 57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 58 | case (wbs_adr_i) 59 | //marker_template_start 60 | //data: ../data/wfg_interconnect_reg.json 61 | //template: wishbone/assign_to_registers.template 62 | //marker_template_code 63 | 64 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 65 | 4'h4: driver0_select_ff <= wbs_dat_i[ 1: 0]; 66 | 4'h8: driver1_select_ff <= wbs_dat_i[ 1: 0]; 67 | 68 | //marker_template_end 69 | default: begin 70 | end 71 | endcase 72 | end 73 | end 74 | 75 | // Wishbone read from slave 76 | always_ff @(posedge wb_clk_i) begin 77 | if (wb_rst_i) begin 78 | wbs_dat_o <= '0; 79 | end else begin 80 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 81 | wbs_dat_o <= '0; // default value 82 | case (wbs_adr_i) 83 | //marker_template_start 84 | //data: ../data/wfg_interconnect_reg.json 85 | //template: wishbone/assign_from_registers.template 86 | //marker_template_code 87 | 88 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 89 | 4'h4: wbs_dat_o[ 1: 0] <= driver0_select_ff; 90 | 4'h8: wbs_dat_o[ 1: 0] <= driver1_select_ff; 91 | 92 | //marker_template_end 93 | default: wbs_dat_o <= 'X; 94 | endcase 95 | end 96 | end 97 | end 98 | 99 | // Acknowledgement 100 | always_ff @(posedge wb_clk_i) begin 101 | if (wb_rst_i) wbs_ack_o <= 1'b0; 102 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 103 | end 104 | 105 | //marker_template_start 106 | //data: ../data/wfg_interconnect_reg.json 107 | //template: wishbone/assign_outputs.template 108 | //marker_template_code 109 | 110 | assign ctrl_en_q_o = ctrl_en_ff; 111 | assign driver0_select_q_o = driver0_select_ff; 112 | assign driver1_select_q_o = driver1_select_ff; 113 | 114 | //marker_template_end 115 | endmodule 116 | `default_nettype wire 117 | -------------------------------------------------------------------------------- /design/wfg_interconnect/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_interconnect_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_interconnect 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_interconnect/sim/view.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI 3 | [*] Wed May 11 08:07:10 2022 4 | [*] 5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/wfg_drive_spi_tb.vcd" 6 | [dumpfile_mtime] "Wed May 11 08:06:37 2022" 7 | [dumpfile_size] 99622 8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/view.gtkw" 9 | [timestart] 0 10 | [size] 1920 1011 11 | [pos] -1 -1 12 | *-23.000000 3060000 -1 -1 -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] wfg_drive_spi_tb. 14 | [treeopen] wfg_drive_spi_tb.wfg_drive_spi_top. 15 | [sst_width] 227 16 | [signals_width] 345 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 295 19 | @200 20 | -Wishbone 21 | @28 22 | wfg_drive_spi_tb.io_wbs_clk 23 | @22 24 | wfg_drive_spi_tb.io_wbs_adr[31:0] 25 | wfg_drive_spi_tb.io_wbs_datrd[31:0] 26 | wfg_drive_spi_tb.io_wbs_datwr[31:0] 27 | @28 28 | wfg_drive_spi_tb.io_wbs_ack 29 | wfg_drive_spi_tb.io_wbs_cyc 30 | wfg_drive_spi_tb.io_wbs_rst 31 | wfg_drive_spi_tb.io_wbs_stb 32 | wfg_drive_spi_tb.io_wbs_we 33 | @200 34 | -AXI 35 | @22 36 | wfg_drive_spi_tb.wfg_drive_spi_axis_tdata[31:0] 37 | @28 38 | wfg_drive_spi_tb.wfg_drive_spi_axis_tlast 39 | wfg_drive_spi_tb.wfg_drive_spi_axis_tready 40 | wfg_drive_spi_tb.wfg_drive_spi_axis_tvalid 41 | @200 42 | -SPI 43 | @29 44 | wfg_drive_spi_tb.wfg_drive_spi_sclk_o 45 | @28 46 | wfg_drive_spi_tb.wfg_drive_spi_cs_no 47 | wfg_drive_spi_tb.wfg_drive_spi_sdo_en_o 48 | wfg_drive_spi_tb.wfg_drive_spi_sdo_o 49 | wfg_drive_spi_tb.wfg_pat_sync_i 50 | wfg_drive_spi_tb.wgf_pat_subcycle_i 51 | @200 52 | -Register 53 | @28 54 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpha_ff 55 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpol_ff 56 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_dff_ff[5:4] 57 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_lsbfirst_ff 58 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_mstr_ff 59 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_oectrl_ff[11:10] 60 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_ssctrl_ff 61 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_sspol_ff 62 | @22 63 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.clkcfg_div_ff[7:0] 64 | @28 65 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.ctrl_en_ff 66 | @22 67 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_peripheral_type_ff[7:0] 68 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_version_ff[15:8] 69 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.reginfo_date_ff[17:0] 70 | @28 71 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.test_lpen_ff 72 | [pattern_trace] 1 73 | [pattern_trace] 0 74 | -------------------------------------------------------------------------------- /design/wfg_interconnect/testbench/test_wfg_interconnect.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import os 5 | import cocotb 6 | from cocotb.clock import Clock 7 | from cocotb.regression import TestFactory 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge 9 | from cocotbext.wishbone.driver import WishboneMaster 10 | from cocotbext.wishbone.driver import WBOp 11 | from cocotbext.axi import AxiStreamBus, AxiStreamSource, AxiStreamSink 12 | from cocotbext.spi import SpiMaster, SpiSignals, SpiConfig, SpiSlaveBase 13 | #from random import randbytes # Possible in Python 3.9+ 14 | 15 | SYSCLK = 100000000 16 | DATA_CNT = 10 17 | NUM_CHANNELS = 2 18 | 19 | short_per = Timer(100, units="ns") 20 | long_time = Timer(100, units="us") 21 | 22 | async def set_register(dut, wbs, address, data): 23 | dut._log.info(f"Set register {address} : {data}") 24 | wbRes = await wbs.send_cycle([WBOp(address, data)]) 25 | rvalues = [wb.datrd for wb in wbRes] 26 | dut._log.info(f"Returned values : {rvalues}") 27 | 28 | async def configure(dut, wbs, en=1, select0=0, select1=0): 29 | await set_register(dut, wbs, 0x8, select1) 30 | await set_register(dut, wbs, 0x4, select0) 31 | await set_register(dut, wbs, 0x0, en) # Enable 32 | 33 | async def receive_data(axis_sink, received_data, cnt): 34 | while 1: 35 | data = await axis_sink.recv() 36 | received_data[cnt].append(data.tdata[0]) 37 | 38 | @cocotb.coroutine 39 | async def test_interconnect(dut, en, select0, select1): 40 | dut._log.info(f"Configuration: en={en}, select0={select0}, select1={select1}") 41 | 42 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start()) 43 | 44 | dut._log.info("Initialize and reset model") 45 | 46 | received_data = [[] for y in range(NUM_CHANNELS)] 47 | 48 | # Start reset 49 | dut.io_wbs_rst.value = 1 50 | dut.stimulus_0.value = 1 51 | await Timer(100, units='ns') 52 | 53 | # Stop reset 54 | dut.io_wbs_rst.value = 0 55 | 56 | # Wishbone Master 57 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 58 | width=32, # size of data bus 59 | timeout=10) # in clock cycle number 60 | 61 | # Setup 62 | await configure(dut, wbs, en=en, select0=select0, select1=select1) 63 | 64 | await short_per 65 | 66 | axis_stimulus_0 = AxiStreamSource(AxiStreamBus.from_prefix(dut, "stimulus_0_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst) 67 | 68 | axis_stimulus_1 = AxiStreamSource(AxiStreamBus.from_prefix(dut, "stimulus_1_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst) 69 | 70 | axis_driver_0 = AxiStreamSink(AxiStreamBus.from_prefix(dut, "driver_0_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst) 71 | 72 | axis_driver_1 = AxiStreamSink(AxiStreamBus.from_prefix(dut, "driver_1_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst) 73 | 74 | cocotb.start_soon(receive_data(axis_driver_0, received_data, 0)) 75 | cocotb.start_soon(receive_data(axis_driver_1, received_data, 1)) 76 | 77 | send_data = [[int.from_bytes(os.getrandom(4, os.GRND_NONBLOCK), "big") for _ in range(DATA_CNT)] for y in range(NUM_CHANNELS)] 78 | 79 | for cnt in range(len(send_data)): 80 | for data in send_data[cnt]: 81 | 82 | if cnt == 0: 83 | await axis_stimulus_0.send([data]) 84 | if cnt == 1: 85 | await axis_stimulus_1.send([data]) 86 | 87 | await short_per 88 | 89 | await short_per 90 | 91 | if select0 == 0 and select1 == 0: 92 | assert(received_data[0] == send_data[0]) 93 | assert(received_data[1] == send_data[0]) 94 | if select0 == 1 and select1 == 0: 95 | assert(received_data[0] == send_data[1]) 96 | assert(received_data[1] == send_data[0]) 97 | if select0 == 0 and select1 == 1: 98 | assert(received_data[0] == send_data[0]) 99 | assert(received_data[1] == send_data[1]) 100 | if select0 == 1 and select1 == 1: 101 | assert(received_data[0] == send_data[1]) 102 | assert(received_data[1] == send_data[1]) 103 | 104 | factory = TestFactory(test_interconnect) 105 | factory.add_option("en", [1]) 106 | factory.add_option("select0", [0, 1]) 107 | factory.add_option("select1", [0, 1]) 108 | 109 | factory.generate_tests() 110 | -------------------------------------------------------------------------------- /design/wfg_interconnect/testbench/wfg_interconnect_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | `ifndef WFG_INTERCONNECT_PKG 13 | `define WFG_INTERCONNECT_PKG 14 | typedef struct packed { 15 | logic wfg_axis_tvalid; 16 | logic [31:0] wfg_axis_tdata; 17 | } axis_t; 18 | `endif 19 | 20 | module wfg_interconnect_tb #( 21 | parameter int BUSW = 32, 22 | parameter int AXIS_DATA_WIDTH = 32 23 | ) ( 24 | // Wishbone interface signals 25 | input io_wbs_clk, 26 | input io_wbs_rst, 27 | input [(BUSW-1):0] io_wbs_adr, 28 | input [(BUSW-1):0] io_wbs_datwr, 29 | output [(BUSW-1):0] io_wbs_datrd, 30 | input io_wbs_we, 31 | input io_wbs_stb, 32 | output io_wbs_ack, 33 | input io_wbs_cyc, 34 | 35 | // Stimuli 36 | input stimulus_0_wfg_axis_tvalid, 37 | input logic [31:0] stimulus_0_wfg_axis_tdata, 38 | 39 | input stimulus_1_wfg_axis_tvalid, 40 | input logic [31:0] stimulus_1_wfg_axis_tdata, 41 | 42 | output logic stimulus_0_wfg_axis_tready, 43 | output logic stimulus_1_wfg_axis_tready, 44 | 45 | // Driver 46 | output driver_0_wfg_axis_tvalid, 47 | output logic [31:0] driver_0_wfg_axis_tdata, 48 | 49 | output driver_1_wfg_axis_tvalid, 50 | output logic [31:0] driver_1_wfg_axis_tdata, 51 | 52 | input driver_0_wfg_axis_tready, 53 | input driver_1_wfg_axis_tready 54 | ); 55 | axis_t stimulus_0; 56 | assign stimulus_0.wfg_axis_tdata = stimulus_0_wfg_axis_tdata; 57 | assign stimulus_0.wfg_axis_tvalid = stimulus_0_wfg_axis_tvalid; 58 | 59 | axis_t stimulus_1; 60 | assign stimulus_1.wfg_axis_tdata = stimulus_1_wfg_axis_tdata; 61 | assign stimulus_1.wfg_axis_tvalid = stimulus_1_wfg_axis_tvalid; 62 | 63 | axis_t driver_0; 64 | assign driver_0_wfg_axis_tdata = driver_0.wfg_axis_tdata; 65 | assign driver_0_wfg_axis_tvalid = driver_0.wfg_axis_tvalid; 66 | 67 | axis_t driver_1; 68 | assign driver_1_wfg_axis_tdata = driver_1.wfg_axis_tdata; 69 | assign driver_1_wfg_axis_tvalid = driver_1.wfg_axis_tvalid; 70 | 71 | 72 | wfg_interconnect_top wfg_interconnect_top ( 73 | .wb_clk_i (io_wbs_clk), 74 | .wb_rst_i (io_wbs_rst), 75 | .wbs_stb_i(io_wbs_stb), 76 | .wbs_cyc_i(io_wbs_cyc), 77 | .wbs_we_i (io_wbs_we), 78 | .wbs_sel_i(4'b1111), 79 | .wbs_dat_i(io_wbs_datwr), 80 | .wbs_adr_i(io_wbs_adr), 81 | .wbs_ack_o(io_wbs_ack), 82 | .wbs_dat_o(io_wbs_datrd), 83 | 84 | .stimulus_0, 85 | .stimulus_1, 86 | 87 | .wfg_axis_tready_stimulus_0(stimulus_0_wfg_axis_tready), 88 | .wfg_axis_tready_stimulus_1(stimulus_1_wfg_axis_tready), 89 | 90 | .driver_0, 91 | .driver_1, 92 | 93 | .wfg_axis_tready_driver_0(driver_0_wfg_axis_tready), 94 | .wfg_axis_tready_driver_1(driver_1_wfg_axis_tready) 95 | ); 96 | 97 | // Dump waves 98 | `ifndef VERILATOR 99 | initial begin 100 | $dumpfile("wfg_interconnect_tb.vcd"); 101 | $dumpvars(0, wfg_interconnect_tb); 102 | end 103 | `endif 104 | 105 | endmodule 106 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/data/wfg_stim_mem_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Control register for memory unit 3 | ,,EN,rw,cfg,0,0,1'b0,Enables stimuli generation 4 | 4'h4,START,,,,,,,Start register 5 | ,,VAL,rw,cfg,15,0,0,Address start value 6 | 4'h8,END,,,,,,,End register 7 | ,,VAL,rw,cfg,15,0,0,Address end value 8 | 4'hC,CFG,,,,,,,Configuration register 9 | ,,INC,rw,cfg,7,0,8'h01,Increment the address with value 10 | ,,GAIN,rw,cfg,23,8,8'h01,Gain value 11 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/data/wfg_stim_mem_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CFG": { 4 | "address": "4'hC", 5 | "description": "Configuration register", 6 | "entries": { 7 | "GAIN": { 8 | "LSB": "8", 9 | "MSB": "23", 10 | "access": "rw", 11 | "description": "Gain value", 12 | "hardware": "cfg", 13 | "reset": "8'h01" 14 | }, 15 | "INC": { 16 | "LSB": "0", 17 | "MSB": "7", 18 | "access": "rw", 19 | "description": "Increment the address with value", 20 | "hardware": "cfg", 21 | "reset": "8'h01" 22 | } 23 | } 24 | }, 25 | "CTRL": { 26 | "address": "4'h0", 27 | "description": "Control register for memory unit", 28 | "entries": { 29 | "EN": { 30 | "LSB": "0", 31 | "MSB": "0", 32 | "access": "rw", 33 | "description": "Enables stimuli generation", 34 | "hardware": "cfg", 35 | "reset": "1'b0" 36 | } 37 | } 38 | }, 39 | "END": { 40 | "address": "4'h8", 41 | "description": "End register", 42 | "entries": { 43 | "VAL": { 44 | "LSB": "0", 45 | "MSB": "15", 46 | "access": "rw", 47 | "description": "Address end value", 48 | "hardware": "cfg", 49 | "reset": "0" 50 | } 51 | } 52 | }, 53 | "START": { 54 | "address": "4'h4", 55 | "description": "Start register", 56 | "entries": { 57 | "VAL": { 58 | "LSB": "0", 59 | "MSB": "15", 60 | "access": "rw", 61 | "description": "Address start value", 62 | "hardware": "cfg", 63 | "reset": "0" 64 | } 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /design/wfg_stim_mem/rtl/wfg_stim_mem.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_stim_mem ( 6 | input wire clk, // clock signal 7 | input wire rst_n, // reset signal 8 | 9 | input wire wfg_axis_tready_i, // ready signal - AXI 10 | output wire wfg_axis_tvalid_o, // valid signal - AXI 11 | output wire [31:0] wfg_axis_tdata_o, // mem output - AXI 12 | 13 | input wire ctrl_en_q_i, // enable/disable 14 | input logic [15:0] end_val_q_i, // END.VAL register output 15 | input logic [15:0] start_val_q_i, // START.VAL register output 16 | input logic [ 7:0] cfg_inc_q_i, // CFG.INC register output 17 | input logic [15:0] cfg_gain_q_i, // CFG.GAIN register output 18 | 19 | // Memory interface 20 | output csb1, 21 | output [ 9:0] addr1, 22 | input logic [31:0] dout1 23 | ); 24 | logic [15:0] cur_address; 25 | logic [31:0] data; 26 | logic [31:0] data_calc; 27 | logic valid; 28 | 29 | assign csb1 = !ctrl_en_q_i; // Enable the memory 30 | assign addr1 = cur_address[9:0]; // Assign address 31 | 32 | assign wfg_axis_tvalid_o = valid; 33 | assign wfg_axis_tdata_o = data; 34 | 35 | typedef enum { 36 | ST_IDLE, 37 | ST_READ, 38 | ST_CALC, 39 | ST_DONE 40 | } wfg_stim_mem_states_t; 41 | 42 | wfg_stim_mem_states_t cur_state; 43 | wfg_stim_mem_states_t next_state; 44 | 45 | always_ff @(posedge clk, negedge rst_n) begin 46 | if (!rst_n) cur_state <= ST_IDLE; 47 | else cur_state <= next_state; 48 | end 49 | 50 | always_comb begin 51 | next_state = cur_state; 52 | case (cur_state) 53 | ST_IDLE: begin 54 | if (ctrl_en_q_i) next_state = ST_READ; 55 | end 56 | ST_READ: begin 57 | next_state = ST_CALC; 58 | end 59 | ST_CALC: begin 60 | next_state = ST_DONE; 61 | end 62 | ST_DONE: begin 63 | if (wfg_axis_tready_i == 1'b1) next_state = ST_IDLE; 64 | end 65 | endcase 66 | end 67 | 68 | always_ff @(posedge clk, negedge rst_n) begin 69 | if (!rst_n) begin 70 | cur_address <= '0; 71 | valid <= '0; 72 | data <= '0; 73 | end else begin 74 | valid <= '0; 75 | 76 | case (cur_state) 77 | ST_IDLE: begin 78 | if (!ctrl_en_q_i) begin 79 | cur_address <= start_val_q_i; 80 | end 81 | end 82 | ST_READ: begin 83 | if (cur_address + cfg_inc_q_i > end_val_q_i) begin 84 | cur_address <= start_val_q_i; 85 | end else begin 86 | cur_address <= cur_address + cfg_inc_q_i; 87 | end 88 | end 89 | ST_CALC: begin 90 | data <= data_calc; 91 | end 92 | ST_DONE: begin 93 | valid <= '1; 94 | end 95 | default: valid <= 'x; 96 | endcase 97 | end 98 | end 99 | 100 | dsp_scale_sn_us #( 101 | .DATA_W(32), 102 | .SCALE_W(16), 103 | .SCALE_SHFT(0) 104 | ) dsp_scale_sn_us_inst ( 105 | .clk (clk), // I; System clock 106 | .reset_ni(rst_n), // I; active loaw reset 107 | 108 | // Data interface 109 | .scale_gif_data_in_update_i(next_state == ST_CALC), // I; GIF update pulse 110 | .scale_gif_data_in_i (dout1), // I; GIF data to be scaled (signed) 111 | .scale_factor_i (cfg_gain_q_i), // I; Scaling factor (unsigned) 112 | .scale_gif_result_update_o (), // O; GIF update pulse 113 | .scale_gif_result_o (data_calc) 114 | ); 115 | 116 | endmodule 117 | `default_nettype wire 118 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/rtl/wfg_stim_mem_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_stim_mem_top #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output wbs_ack_o, 18 | output [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // AXI-Stream interface 21 | input wfg_axis_tready_i, 22 | output wfg_axis_tvalid_o, 23 | output [31:0] wfg_axis_tdata_o, 24 | 25 | // Memory interface 26 | output csb1, 27 | output [ 9:0] addr1, 28 | input [31:0] dout1 29 | ); 30 | // Registers 31 | //marker_template_start 32 | //data: ../data/wfg_stim_mem_reg.json 33 | //template: wishbone/instantiate_top.template 34 | //marker_template_code 35 | 36 | logic [23: 8] cfg_gain_q; // CFG.GAIN register output 37 | logic [ 7: 0] cfg_inc_q; // CFG.INC register output 38 | logic ctrl_en_q; // CTRL.EN register output 39 | logic [15: 0] end_val_q; // END.VAL register output 40 | logic [15: 0] start_val_q; // START.VAL register output 41 | 42 | //marker_template_end 43 | 44 | wfg_stim_mem_wishbone_reg wfg_stim_mem_wishbone_reg ( 45 | .wb_clk_i (wb_clk_i), 46 | .wb_rst_i (wb_rst_i), 47 | .wbs_stb_i(wbs_stb_i), 48 | .wbs_cyc_i(wbs_cyc_i), 49 | .wbs_we_i (wbs_we_i), 50 | .wbs_sel_i(wbs_sel_i), 51 | .wbs_dat_i(wbs_dat_i), 52 | .wbs_adr_i(wbs_adr_i), 53 | .wbs_ack_o(wbs_ack_o), 54 | .wbs_dat_o(wbs_dat_o), 55 | 56 | //marker_template_start 57 | //data: ../data/wfg_stim_mem_reg.json 58 | //template: wishbone/assign_to_module.template 59 | //marker_template_code 60 | 61 | .cfg_gain_q_o (cfg_gain_q), // CFG.GAIN register output 62 | .cfg_inc_q_o (cfg_inc_q), // CFG.INC register output 63 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output 64 | .end_val_q_o (end_val_q), // END.VAL register output 65 | .start_val_q_o(start_val_q) // START.VAL register output 66 | 67 | //marker_template_end 68 | ); 69 | 70 | wfg_stim_mem wfg_stim_mem ( 71 | .clk (wb_clk_i), // clock signal 72 | .rst_n (!wb_rst_i), // reset signal 73 | .wfg_axis_tready_i(wfg_axis_tready_i), // ready signal - AXI 74 | .wfg_axis_tvalid_o(wfg_axis_tvalid_o), // valid signal - AXI 75 | .wfg_axis_tdata_o (wfg_axis_tdata_o), // mem output - AXI 76 | .ctrl_en_q_i (ctrl_en_q), // enable/disable 77 | .end_val_q_i (end_val_q), 78 | .start_val_q_i (start_val_q), 79 | .cfg_inc_q_i (cfg_inc_q), 80 | .cfg_gain_q_i (cfg_gain_q), 81 | .csb1 (csb1), 82 | .addr1 (addr1), 83 | .dout1 (dout1) 84 | ); 85 | 86 | endmodule 87 | `default_nettype wire 88 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/rtl/wfg_stim_mem_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_stim_mem_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_stim_mem_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic [23:8] cfg_gain_q_o, // CFG.GAIN register output 27 | output logic [ 7:0] cfg_inc_q_o, // CFG.INC register output 28 | output logic ctrl_en_q_o, // CTRL.EN register output 29 | output logic [15:0] end_val_q_o, // END.VAL register output 30 | output logic [15:0] start_val_q_o // START.VAL register output 31 | 32 | //marker_template_end 33 | ); 34 | 35 | //marker_template_start 36 | //data: ../data/wfg_stim_mem_reg.json 37 | //template: wishbone/instantiate_registers.template 38 | //marker_template_code 39 | 40 | logic [23: 8] cfg_gain_ff; // CFG.GAIN FF 41 | logic [ 7: 0] cfg_inc_ff; // CFG.INC FF 42 | logic ctrl_en_ff; // CTRL.EN FF 43 | logic [15: 0] end_val_ff; // END.VAL FF 44 | logic [15: 0] start_val_ff; // START.VAL FF 45 | 46 | //marker_template_end 47 | 48 | // Wishbone write to slave 49 | always_ff @(posedge wb_clk_i) begin 50 | if (wb_rst_i) begin 51 | //marker_template_start 52 | //data: ../data/wfg_stim_mem_reg.json 53 | //template: wishbone/reset_registers.template 54 | //marker_template_code 55 | 56 | cfg_gain_ff <= 8'h01; 57 | cfg_inc_ff <= 8'h01; 58 | ctrl_en_ff <= 1'b0; 59 | end_val_ff <= 0; 60 | start_val_ff <= 0; 61 | 62 | //marker_template_end 63 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 64 | case (wbs_adr_i) 65 | //marker_template_start 66 | //data: ../data/wfg_stim_mem_reg.json 67 | //template: wishbone/assign_to_registers.template 68 | //marker_template_code 69 | 70 | 4'hC: begin 71 | cfg_gain_ff <= wbs_dat_i[23:8]; 72 | cfg_inc_ff <= wbs_dat_i[7:0]; 73 | end 74 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 75 | 4'h8: end_val_ff <= wbs_dat_i[15: 0]; 76 | 4'h4: start_val_ff <= wbs_dat_i[15: 0]; 77 | 78 | //marker_template_end 79 | default: begin 80 | end 81 | endcase 82 | end 83 | end 84 | 85 | // Wishbone read from slave 86 | always_ff @(posedge wb_clk_i) begin 87 | if (wb_rst_i) begin 88 | wbs_dat_o <= '0; 89 | end else begin 90 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 91 | wbs_dat_o <= '0; // default value 92 | case (wbs_adr_i) 93 | //marker_template_start 94 | //data: ../data/wfg_stim_mem_reg.json 95 | //template: wishbone/assign_from_registers.template 96 | //marker_template_code 97 | 98 | 4'hC: begin 99 | wbs_dat_o[23:8] <= cfg_gain_ff; 100 | wbs_dat_o[7:0] <= cfg_inc_ff; 101 | end 102 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 103 | 4'h8: wbs_dat_o[15: 0] <= end_val_ff; 104 | 4'h4: wbs_dat_o[15: 0] <= start_val_ff; 105 | 106 | //marker_template_end 107 | default: wbs_dat_o <= 'X; 108 | endcase 109 | end 110 | end 111 | end 112 | 113 | // Acknowledgement 114 | always_ff @(posedge wb_clk_i) begin 115 | if (wb_rst_i) wbs_ack_o <= 1'b0; 116 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 117 | end 118 | 119 | //marker_template_start 120 | //data: ../data/wfg_stim_mem_reg.json 121 | //template: wishbone/assign_outputs.template 122 | //marker_template_code 123 | 124 | assign cfg_gain_q_o = cfg_gain_ff; 125 | assign cfg_inc_q_o = cfg_inc_ff; 126 | assign ctrl_en_q_o = ctrl_en_ff; 127 | assign end_val_q_o = end_val_ff; 128 | assign start_val_q_o = start_val_ff; 129 | 130 | //marker_template_end 131 | endmodule 132 | `default_nettype wire 133 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_stim_mem_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_stim_mem 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/sim/memory.hex: -------------------------------------------------------------------------------- 1 | 00000000 2 | 00000001 3 | 00000002 4 | 00000003 5 | 00000004 6 | 00000005 7 | 00000006 8 | 00000007 9 | 00000008 10 | 00000009 11 | 0000000A 12 | 0000000B 13 | 0000000C 14 | 0000000D 15 | 0000000E 16 | 0000000F 17 | 00000010 18 | 00000011 19 | 00000012 20 | 00000013 21 | 00000014 22 | 00000015 23 | 00000016 24 | 00000017 25 | 00000018 26 | 00000019 27 | 0000001A 28 | 0000001B 29 | 0000001C 30 | 0000001D 31 | 0000001E 32 | 0000001F 33 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/testbench/test_wfg_stim_mem.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import math 5 | import statistics 6 | import cocotb 7 | from cocotb.clock import Clock 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge 9 | from cocotb.regression import TestFactory 10 | from cocotbext.wishbone.driver import WishboneMaster 11 | from cocotbext.wishbone.driver import WBOp 12 | 13 | DATA_CNT = 10 14 | 15 | short_per = Timer(100, units="ns") 16 | long_time = Timer(100, units="us") 17 | 18 | async def set_register(dut, wbs, peripheral_address, address, data): 19 | if address > 0xF: 20 | dut._log.error("Can not access peripheral registers outside 0xF") 21 | 22 | real_address = (peripheral_address<<4) | (address & 0xF) 23 | dut._log.info(f"Set register {real_address} : {data}") 24 | 25 | wbRes = await wbs.send_cycle([WBOp(real_address, data)]) 26 | rvalues = [wb.datrd for wb in wbRes] 27 | dut._log.info(f"Returned values : {rvalues}") 28 | 29 | async def configure_stim_mem(dut, wbs, en, start=0x0000, end=0x0000, inc=0x01, gain=0x0001): 30 | await set_register(dut, wbs, 0x0, 0x4, start) 31 | await set_register(dut, wbs, 0x0, 0x8, end) 32 | await set_register(dut, wbs, 0x0, 0xC, gain<<8 | inc) 33 | await set_register(dut, wbs, 0x0, 0x0, en) # Enable 34 | 35 | @cocotb.coroutine 36 | async def mem_test(dut, start=0x0000, end=0x0000, inc=0x01, gain=0x0001): 37 | cocotb.start_soon(Clock(dut.io_wbs_clk, 10, units="ns").start()) 38 | 39 | dut._log.info("Initialize and reset model") 40 | 41 | # Start reset 42 | dut.io_wbs_rst.value = 1 43 | dut.wfg_axis_tready.value = 1 44 | 45 | await Timer(100, units='ns') 46 | 47 | # Stop reset 48 | dut.io_wbs_rst.value = 0 49 | 50 | # Wishbone Master 51 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 52 | width=32, # size of data bus 53 | timeout=10) # in clock cycle number 54 | 55 | await short_per 56 | 57 | dut._log.info("Configure stim_mem") 58 | await configure_stim_mem(dut, wbs, en=1, start=start, end=end, inc=inc, gain=gain) 59 | 60 | cur_address = start 61 | 62 | # Gather data 63 | for i in range(DATA_CNT): 64 | await FallingEdge(dut.wfg_axis_tvalid) 65 | value = dut.wfg_axis_tdata.value 66 | 67 | dut._log.info(f"Test: {cur_address} == {value}") 68 | 69 | assert(cur_address*gain == value) 70 | 71 | cur_address += inc 72 | 73 | if (cur_address > end): 74 | cur_address = start 75 | 76 | factory = TestFactory(mem_test) 77 | 78 | factory.add_option("start", [0x0000, 0x0010]) 79 | factory.add_option("end", [0x0005, 0x001F]) 80 | factory.add_option("inc", [0x01, 0x02]) 81 | factory.add_option("gain", [0x01, 0x04]) 82 | 83 | factory.generate_tests() 84 | -------------------------------------------------------------------------------- /design/wfg_stim_mem/testbench/wfg_stim_mem_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_stim_mem_tb #( 13 | parameter int BUSW = 32 14 | ) ( 15 | // Wishbone interface signals 16 | input io_wbs_clk, 17 | input io_wbs_rst, 18 | input [(BUSW-1):0] io_wbs_adr, 19 | input [(BUSW-1):0] io_wbs_datwr, 20 | output [(BUSW-1):0] io_wbs_datrd, 21 | input io_wbs_we, 22 | input io_wbs_stb, 23 | output io_wbs_ack, 24 | input io_wbs_cyc, 25 | 26 | // AXI-Stream interface 27 | input wfg_axis_tready, 28 | output wfg_axis_tvalid, 29 | output [31:0] wfg_axis_tdata 30 | ); 31 | 32 | logic csb1; 33 | logic [9:0 ] addr1; 34 | logic [31:0] dout1; 35 | 36 | localparam MEM_SIZE = 2 ** 10; 37 | 38 | logic [31:0] mem[MEM_SIZE]; 39 | 40 | wfg_stim_mem_top wfg_stim_mem_top ( 41 | .wb_clk_i (io_wbs_clk), 42 | .wb_rst_i (io_wbs_rst), 43 | .wbs_stb_i(io_wbs_stb), 44 | .wbs_cyc_i(io_wbs_cyc), 45 | .wbs_we_i (io_wbs_we), 46 | .wbs_sel_i(4'b1111), 47 | .wbs_dat_i(io_wbs_datwr), 48 | .wbs_adr_i(io_wbs_adr), 49 | .wbs_ack_o(io_wbs_ack), 50 | .wbs_dat_o(io_wbs_datrd), 51 | 52 | .wfg_axis_tready_i(wfg_axis_tready), 53 | .wfg_axis_tvalid_o(wfg_axis_tvalid), 54 | .wfg_axis_tdata_o (wfg_axis_tdata), 55 | 56 | .csb1 (csb1), 57 | .addr1(addr1), 58 | .dout1(dout1) 59 | ); 60 | 61 | initial begin 62 | $readmemh("memory.hex", mem); 63 | end 64 | 65 | always_ff @(negedge io_wbs_clk) begin 66 | if (!csb1) dout1 <= mem[addr1]; 67 | end 68 | 69 | // Dump waves 70 | `ifndef VERILATOR 71 | initial begin 72 | $dumpfile("wfg_stim_mem_tb.vcd"); 73 | $dumpvars(0, wfg_stim_mem_tb); 74 | end 75 | `endif 76 | 77 | endmodule 78 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Overview 4 | 5 | The Sine Wave Pattern Generator serves as a data generator that provides data for the AXI streaming interface. The sine wave generator, as the name implies, generates values in the form of sine waves. The values are generated from the angular phase, which is controlled by the `inc` (increment) register. Other parameters that influence the sine wave are gain, multiplier with a range of 0 - 1.999..., and offset with a range of -2 to 1.999.... 6 | 7 | ## RTL Implementation - CORDIC Algorithm 8 | 9 | CORDIC (COordinate Rotation DIgital Computer) is a hardware-efficient iterative method that uses rotations to compute a wide range of elementary functions. The RTL uses the CORDIC algorithm as a primitive method for generating sinusoidal values from phase. The behavior of the CORDIC algorithm is controlled by the following input registers: 10 | - inc_val_q_i (16bit) - phase increment value 11 | - gain_val_q_i (16bit) - gain value (0 - 1.999..) 12 | - offset_val_q_i (18bit) - offset value (-2 - 1.999..) 13 | 14 | The output is represented by the following registers: 15 | - sine_out (18bit) - sine value (-2 - 1.999...) calculated from the input parameters. 16 | 17 | Some other inputs are: 18 | - clk - system clock 19 | - rst - reset 20 | - ctrl_en_q_i - enable/disable data generation 21 | 22 | The output is a 2f16 signed value with a frequency of 100*MHz*. 23 | 24 | ## Cocotb Testbench 25 | 26 | Cocotb is used for the testbench environment. It allows the user to access all RTL signals, control them and influence their behavior. In this case, the testbench accesses and modifies the phase increment value (`inc_val_q_i`), the gain factor (`gain_val_q_i`) and the offset (`offset_val_q_i`), as well as the system clock, reset and data generation enable. On the other hand, the testbench receives sine values from RTL via the `sine_out` output line. Just as RTL performs the calculation of the sine value using the CORDIC algorithm, the Testbench can import the *sin()* function from the *math* library. With the corresponding calculation, the sine value is output in hexadecimal form. The verification of the values calculated in the testbench and the values from the CORDIC algorithm is explained in detail in the following section. -------------------------------------------------------------------------------- /design/wfg_stim_sine/data/wfg_stim_sine_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Control register for sine wave generation unit 3 | ,,EN,rw,cfg,0,0,1'b0,Enables stimuli generation 4 | 4'h4,INC,,,,,,,Increment register 5 | ,,VAL,rw,cfg,15,0,16'h1000,Increment for angle per sample (f=fs/2**16/INC). Counter is implemented as wrap-around counter. 6 | 4'h8,GAIN,,,,,,,Gain register 7 | ,,VAL,rw,cfg,15,0,16'h4000,Sine wave gain in 2f14 unsigned representation. 8 | 4'hC,OFFSET,,,,,,,Offset register 9 | ,,VAL,rw,cfg,17,0,18'h0000,Signed offset added after applying gain. 10 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/data/wfg_stim_sine_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CTRL": { 4 | "address": "4'h0", 5 | "description": "Control register for sine wave generation unit", 6 | "entries": { 7 | "EN": { 8 | "LSB": "0", 9 | "MSB": "0", 10 | "access": "rw", 11 | "description": "Enables stimuli generation", 12 | "hardware": "cfg", 13 | "reset": "1'b0" 14 | } 15 | } 16 | }, 17 | "GAIN": { 18 | "address": "4'h8", 19 | "description": "Gain register", 20 | "entries": { 21 | "VAL": { 22 | "LSB": "0", 23 | "MSB": "15", 24 | "access": "rw", 25 | "description": "Sine wave gain in 2f14 unsigned representation. ", 26 | "hardware": "cfg", 27 | "reset": "16'h4000" 28 | } 29 | } 30 | }, 31 | "INC": { 32 | "address": "4'h4", 33 | "description": "Increment register", 34 | "entries": { 35 | "VAL": { 36 | "LSB": "0", 37 | "MSB": "15", 38 | "access": "rw", 39 | "description": "Increment for angle per sample (f=fs/2**16/INC). Counter is implemented as wrap-around counter.", 40 | "hardware": "cfg", 41 | "reset": "16'h1000" 42 | } 43 | } 44 | }, 45 | "OFFSET": { 46 | "address": "4'hC", 47 | "description": "Offset register", 48 | "entries": { 49 | "VAL": { 50 | "LSB": "0", 51 | "MSB": "17", 52 | "access": "rw", 53 | "description": "Signed offset added after applying gain.", 54 | "hardware": "cfg", 55 | "reset": "18'h0000" 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /design/wfg_stim_sine/rtl/wfg_stim_sine_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_stim_sine_top #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output wbs_ack_o, 18 | output [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // AXI-Stream interface 21 | input wfg_axis_tready_i, 22 | output wfg_axis_tvalid_o, 23 | output signed [31:0] wfg_axis_tdata_o 24 | ); 25 | // Registers 26 | //marker_template_start 27 | //data: ../data/wfg_stim_sine_reg.json 28 | //template: wishbone/instantiate_top.template 29 | //marker_template_code 30 | 31 | logic ctrl_en_q; // CTRL.EN register output 32 | logic [15: 0] gain_val_q; // GAIN.VAL register output 33 | logic [15: 0] inc_val_q; // INC.VAL register output 34 | logic [17: 0] offset_val_q; // OFFSET.VAL register output 35 | 36 | //marker_template_end 37 | 38 | wfg_stim_sine_wishbone_reg wfg_stim_sine_wishbone_reg ( 39 | .wb_clk_i (wb_clk_i), 40 | .wb_rst_i (wb_rst_i), 41 | .wbs_stb_i(wbs_stb_i), 42 | .wbs_cyc_i(wbs_cyc_i), 43 | .wbs_we_i (wbs_we_i), 44 | .wbs_sel_i(wbs_sel_i), 45 | .wbs_dat_i(wbs_dat_i), 46 | .wbs_adr_i(wbs_adr_i), 47 | .wbs_ack_o(wbs_ack_o), 48 | .wbs_dat_o(wbs_dat_o), 49 | 50 | //marker_template_start 51 | //data: ../data/wfg_stim_sine_reg.json 52 | //template: wishbone/assign_to_module.template 53 | //marker_template_code 54 | 55 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output 56 | .gain_val_q_o (gain_val_q), // GAIN.VAL register output 57 | .inc_val_q_o (inc_val_q), // INC.VAL register output 58 | .offset_val_q_o(offset_val_q) // OFFSET.VAL register output 59 | 60 | //marker_template_end 61 | ); 62 | 63 | wfg_stim_sine wfg_stim_sine ( 64 | .clk (wb_clk_i), // clock signal 65 | .rst_n (!wb_rst_i), // reset signal 66 | .wfg_axis_tready_i(wfg_axis_tready_i), // ready signal - AXI 67 | .wfg_axis_tvalid_o(wfg_axis_tvalid_o), // valid signal - AXI 68 | .wfg_axis_tdata_o (wfg_axis_tdata_o), // sine output - AXI 69 | .ctrl_en_q_i (ctrl_en_q), // enable/disable simulation 70 | .inc_val_q_i (inc_val_q), // angular increment 71 | .gain_val_q_i (gain_val_q), // sine gain/multiplier 72 | .offset_val_q_i (offset_val_q) // sine offset 73 | ); 74 | 75 | endmodule 76 | `default_nettype wire 77 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/rtl/wfg_stim_sine_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_stim_sine_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_stim_sine_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic ctrl_en_q_o, // CTRL.EN register output 27 | output logic [15:0] gain_val_q_o, // GAIN.VAL register output 28 | output logic [15:0] inc_val_q_o, // INC.VAL register output 29 | output logic [17:0] offset_val_q_o // OFFSET.VAL register output 30 | 31 | //marker_template_end 32 | ); 33 | 34 | //marker_template_start 35 | //data: ../data/wfg_stim_sine_reg.json 36 | //template: wishbone/instantiate_registers.template 37 | //marker_template_code 38 | 39 | logic ctrl_en_ff; // CTRL.EN FF 40 | logic [15: 0] gain_val_ff; // GAIN.VAL FF 41 | logic [15: 0] inc_val_ff; // INC.VAL FF 42 | logic [17: 0] offset_val_ff; // OFFSET.VAL FF 43 | 44 | //marker_template_end 45 | 46 | // Wishbone write to slave 47 | always_ff @(posedge wb_clk_i) begin 48 | if (wb_rst_i) begin 49 | //marker_template_start 50 | //data: ../data/wfg_stim_sine_reg.json 51 | //template: wishbone/reset_registers.template 52 | //marker_template_code 53 | 54 | ctrl_en_ff <= 1'b0; 55 | gain_val_ff <= 16'h4000; 56 | inc_val_ff <= 16'h1000; 57 | offset_val_ff <= 18'h0000; 58 | 59 | //marker_template_end 60 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 61 | case (wbs_adr_i) 62 | //marker_template_start 63 | //data: ../data/wfg_stim_sine_reg.json 64 | //template: wishbone/assign_to_registers.template 65 | //marker_template_code 66 | 67 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 68 | 4'h8: gain_val_ff <= wbs_dat_i[15: 0]; 69 | 4'h4: inc_val_ff <= wbs_dat_i[15: 0]; 70 | 4'hC: offset_val_ff <= wbs_dat_i[17: 0]; 71 | 72 | //marker_template_end 73 | default: begin 74 | end 75 | endcase 76 | end 77 | end 78 | 79 | // Wishbone read from slave 80 | always_ff @(posedge wb_clk_i) begin 81 | if (wb_rst_i) begin 82 | wbs_dat_o <= '0; 83 | end else begin 84 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 85 | wbs_dat_o <= '0; // default value 86 | case (wbs_adr_i) 87 | //marker_template_start 88 | //data: ../data/wfg_stim_sine_reg.json 89 | //template: wishbone/assign_from_registers.template 90 | //marker_template_code 91 | 92 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 93 | 4'h8: wbs_dat_o[15: 0] <= gain_val_ff; 94 | 4'h4: wbs_dat_o[15: 0] <= inc_val_ff; 95 | 4'hC: wbs_dat_o[17: 0] <= offset_val_ff; 96 | 97 | //marker_template_end 98 | default: wbs_dat_o <= 'X; 99 | endcase 100 | end 101 | end 102 | end 103 | 104 | // Acknowledgement 105 | always_ff @(posedge wb_clk_i) begin 106 | if (wb_rst_i) wbs_ack_o <= 1'b0; 107 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 108 | end 109 | 110 | //marker_template_start 111 | //data: ../data/wfg_stim_sine_reg.json 112 | //template: wishbone/assign_outputs.template 113 | //marker_template_code 114 | 115 | assign ctrl_en_q_o = ctrl_en_ff; 116 | assign gain_val_q_o = gain_val_ff; 117 | assign inc_val_q_o = inc_val_ff; 118 | assign offset_val_q_o = offset_val_ff; 119 | 120 | //marker_template_end 121 | endmodule 122 | `default_nettype wire 123 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_stim_sine_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_stim_sine 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/testbench/test_wfg_stim_sine.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import math 5 | import statistics 6 | import cocotb 7 | from cocotb.clock import Clock 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge 9 | from cocotb.regression import TestFactory 10 | from cocotbext.wishbone.driver import WishboneMaster 11 | from cocotbext.wishbone.driver import WBOp 12 | 13 | DATA_CNT = 10 14 | 15 | short_per = Timer(100, units="ns") 16 | long_time = Timer(100, units="us") 17 | 18 | async def set_register(dut, wbs, peripheral_address, address, data): 19 | if address > 0xF: 20 | dut._log.error("Can not access peripheral registers outside 0xF") 21 | 22 | real_address = (peripheral_address<<4) | (address & 0xF) 23 | dut._log.info(f"Set register {real_address} : {data}") 24 | 25 | wbRes = await wbs.send_cycle([WBOp(real_address, data)]) 26 | rvalues = [wb.datrd for wb in wbRes] 27 | dut._log.info(f"Returned values : {rvalues}") 28 | 29 | async def configure_stim_sine(dut, wbs, en, inc=0x1000, gain=0x4000, offset=0): 30 | await set_register(dut, wbs, 0x0, 0x4, inc) 31 | await set_register(dut, wbs, 0x0, 0x8, gain) 32 | await set_register(dut, wbs, 0x0, 0xC, offset) 33 | await set_register(dut, wbs, 0x0, 0x0, en) # Enable 34 | 35 | @cocotb.coroutine 36 | async def sine_test(dut, sine_inc=0x1000, sine_gain=0x4000, sine_offset=0): 37 | cocotb.start_soon(Clock(dut.io_wbs_clk, 10, units="ns").start()) 38 | 39 | dut._log.info("Initialize and reset model") 40 | 41 | # Start reset 42 | dut.io_wbs_rst.value = 1 43 | dut.wfg_axis_tready.value = 1 44 | 45 | await Timer(100, units='ns') 46 | 47 | # Stop reset 48 | dut.io_wbs_rst.value = 0 49 | 50 | # Wishbone Master 51 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 52 | width=32, # size of data bus 53 | timeout=10) # in clock cycle number 54 | 55 | await short_per 56 | 57 | dut._log.info("Configure stim_sine") 58 | await configure_stim_sine(dut, wbs, en=1, inc=sine_inc, gain=sine_gain, offset=sine_offset) 59 | 60 | num_values = int((2**16) / sine_inc + 1) 61 | y_data = [] 62 | 63 | # Gather data 64 | for i in range(num_values): 65 | await FallingEdge(dut.wfg_axis_tvalid) 66 | value = int(dut.wfg_axis_tdata.value) 67 | 68 | # Sign extend 69 | if value & (1<<17): 70 | value |= ((1<<14)-1)<<18 71 | 72 | value = value.to_bytes(4, 'big') 73 | value = int.from_bytes(value, 'big', signed=True) 74 | y_data.append(value) 75 | 76 | y_data_float = [] 77 | 78 | y_error = [] 79 | y_squared_error = [] 80 | y_absolute_error = [] 81 | 82 | # Compare results 83 | for (cnt, value) in enumerate(y_data): 84 | input_val = cnt * sine_inc 85 | while input_val >= (2**16): 86 | input_val -= (2**16) 87 | 88 | angle_rad = float(input_val) / (2**16) * 2 * math.pi 89 | calculated_value = math.sin(angle_rad) * (sine_gain / 2**14) + (sine_offset/2**16) 90 | output_as_float = float(value) / (2**16) 91 | y_data_float.append(output_as_float) 92 | y_error.append(output_as_float - calculated_value) 93 | y_squared_error.append((output_as_float - calculated_value)**2) 94 | y_absolute_error.append(abs(output_as_float - calculated_value)) 95 | 96 | y_mean_squared_error = statistics.mean(y_squared_error) 97 | dut._log.info("y_mean_squared_error: {}".format(y_mean_squared_error)) 98 | 99 | y_mean_absolute_error = statistics.mean(y_absolute_error) 100 | dut._log.info("y_mean_absolute_error: {}".format(y_mean_absolute_error)) 101 | 102 | assert(y_mean_absolute_error < 0.001) 103 | 104 | factory = TestFactory(sine_test) 105 | 106 | factory.add_option("sine_inc", [0x500, 0x1000]) 107 | factory.add_option("sine_gain", [0x4000, 0x2000, 0x6000]) 108 | factory.add_option("sine_offset", [0x0000, 0x8000]) 109 | 110 | factory.generate_tests() 111 | -------------------------------------------------------------------------------- /design/wfg_stim_sine/testbench/wfg_stim_sine_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_stim_sine_tb #( 13 | parameter int BUSW = 32 14 | ) ( 15 | // Wishbone interface signals 16 | input io_wbs_clk, 17 | input io_wbs_rst, 18 | input [(BUSW-1):0] io_wbs_adr, 19 | input [(BUSW-1):0] io_wbs_datwr, 20 | output [(BUSW-1):0] io_wbs_datrd, 21 | input io_wbs_we, 22 | input io_wbs_stb, 23 | output io_wbs_ack, 24 | input io_wbs_cyc, 25 | 26 | // AXI-Stream interface 27 | input wfg_axis_tready, 28 | output wfg_axis_tvalid, 29 | output signed [17:0] wfg_axis_tdata 30 | ); 31 | 32 | wfg_stim_sine_top wfg_stim_sine_top ( 33 | .wb_clk_i (io_wbs_clk), 34 | .wb_rst_i (io_wbs_rst), 35 | .wbs_stb_i(io_wbs_stb), 36 | .wbs_cyc_i(io_wbs_cyc), 37 | .wbs_we_i (io_wbs_we), 38 | .wbs_sel_i(4'b1111), 39 | .wbs_dat_i(io_wbs_datwr), 40 | .wbs_adr_i(io_wbs_adr), 41 | .wbs_ack_o(io_wbs_ack), 42 | .wbs_dat_o(io_wbs_datrd), 43 | 44 | .wfg_axis_tready_i(wfg_axis_tready), 45 | .wfg_axis_tvalid_o(wfg_axis_tvalid), 46 | .wfg_axis_tdata_o (wfg_axis_tdata) 47 | ); 48 | 49 | // Dump waves 50 | `ifndef VERILATOR 51 | initial begin 52 | $dumpfile("wfg_stim_sine_tb.vcd"); 53 | $dumpvars(0, wfg_stim_sine_tb); 54 | end 55 | `endif 56 | 57 | endmodule 58 | -------------------------------------------------------------------------------- /design/wfg_subcore/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | *Copyright © 2021* [Semify EDA]( 4 | https://github.com/semify-eda) 5 | 6 | ## wfg_subcore documentation 7 | #### Overview 8 | This module is responsible to synchronize all implemented modules, ensuring data transfers with a set frequency. 9 | According to the configuration parameter wfg_pat_subcycle, clock cycles are counted and a subcycle pulse is generated. This subcycle enables finer division of the synchronization pulse and is used for synchronizations with phase shifts. The configuration parameter wfg_pat_sync specifies the number of subcycle pulses during a sync period. The resulting sync frequency 10 | 11 | ![Formula](formula.png) 12 | 13 | can be determined using these two parameters. The outputs of the module are subcycle and sync pulses, as well as three optional status signals (active, start pulse, subcyle counter). -------------------------------------------------------------------------------- /design/wfg_subcore/data/wfg_subcore_reg.csv: -------------------------------------------------------------------------------- 1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description 2 | 4'h0,CTRL,,,,,,,Core control register 3 | ,,EN,rw,cfg,0,0,1'b0,Core enable 4 | 4'h4,CFG,,,,,,,Core configuration register 5 | ,,SYNC,rw,cfg,7,0,0,"Sync pulse clock divider. 6 | Count clk until threshold is reached" 7 | ,,SUBCYCLE,rw,cfg,23,8,0,"Subcycle pulse clock divider. 8 | Count clk until threshold is reached" 9 | -------------------------------------------------------------------------------- /design/wfg_subcore/data/wfg_subcore_reg.json: -------------------------------------------------------------------------------- 1 | { 2 | "registers": { 3 | "CFG": { 4 | "address": "4'h4", 5 | "description": "Core configuration register", 6 | "entries": { 7 | "SUBCYCLE": { 8 | "LSB": "8", 9 | "MSB": "23", 10 | "access": "rw", 11 | "description": "Subcycle pulse clock divider.\nCount clk until threshold is reached", 12 | "hardware": "cfg", 13 | "reset": "0" 14 | }, 15 | "SYNC": { 16 | "LSB": "0", 17 | "MSB": "7", 18 | "access": "rw", 19 | "description": "Sync pulse clock divider.\nCount clk until threshold is reached", 20 | "hardware": "cfg", 21 | "reset": "0" 22 | } 23 | } 24 | }, 25 | "CTRL": { 26 | "address": "4'h0", 27 | "description": "Core control register", 28 | "entries": { 29 | "EN": { 30 | "LSB": "0", 31 | "MSB": "0", 32 | "access": "rw", 33 | "description": "Core enable", 34 | "hardware": "cfg", 35 | "reset": "1'b0" 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /design/wfg_subcore/formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_subcore/formula.png -------------------------------------------------------------------------------- /design/wfg_subcore/rtl/wfg_subcore.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_subcore ( 6 | input wire clk, // I; System clock 7 | input wire rst_n, // I; Active low reset 8 | input wire en_i, // I; Enable signal 9 | 10 | // Config 11 | input wire [ 7:0] wfg_sync_count_i, // I; Sync counter threshold 12 | input wire [15:0] wfg_subcycle_count_i, // I; Subcycle counter threshold 13 | 14 | // Signals 15 | output wire wfg_subcore_sync_o, // O; Sync signal 16 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal 17 | output wire wfg_subcore_start_o, // O; Indicate start 18 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter 19 | output wire active_o // O; Active indication signal 20 | ); 21 | 22 | // ------------------------------------------------------------------------- 23 | // Definition 24 | // ------------------------------------------------------------------------- 25 | 26 | logic [15:0] subcycle_count; // L; counting variable 27 | logic [ 7:0] sync_count; // L; counting variable 28 | logic temp_subcycle; // L; Internal subcycle 29 | logic temp_sync; // L; Internal subcycle 30 | logic subcycle_dly; // L; Rising edge det subcycle 31 | logic sync_dly; // L; Rising edge det sync 32 | logic en_i_dly; // L; Rising edge det enable 33 | logic [ 7:0] subcycle_pls_cnt; // L; Counts Subcycles 34 | 35 | // ------------------------------------------------------------------------- 36 | // Implementation 37 | // ------------------------------------------------------------------------- 38 | 39 | always @(posedge clk, negedge rst_n) begin 40 | 41 | if (~rst_n) begin 42 | subcycle_count <= 16'd0; 43 | sync_count <= 8'd0; 44 | temp_subcycle <= 1'b0; 45 | temp_sync <= 1'b0; 46 | subcycle_pls_cnt <= 8'd0; 47 | end else begin 48 | 49 | if (en_i) begin 50 | 51 | if (subcycle_count > 0) begin 52 | temp_subcycle <= temp_subcycle; 53 | subcycle_count <= subcycle_count - 1; 54 | end else begin 55 | temp_subcycle <= ~temp_subcycle; 56 | subcycle_count <= (wfg_subcycle_count_i); 57 | 58 | if (sync_count > 0) begin 59 | temp_sync <= temp_sync; 60 | sync_count <= sync_count - 1; 61 | end else begin 62 | temp_sync <= ~temp_sync; 63 | sync_count <= (wfg_sync_count_i); 64 | end 65 | 66 | end 67 | 68 | if (wfg_subcore_subcycle_o) begin 69 | subcycle_pls_cnt <= subcycle_pls_cnt + 1; 70 | end 71 | 72 | end else begin 73 | subcycle_count <= 8'd0; 74 | sync_count <= 8'd0; 75 | temp_subcycle <= 1'b0; 76 | temp_sync <= 1'b0; 77 | subcycle_pls_cnt <= 8'd0; 78 | end 79 | 80 | subcycle_dly <= temp_subcycle; 81 | sync_dly <= temp_sync; 82 | en_i_dly <= en_i; 83 | 84 | if (wfg_subcore_sync_o) begin 85 | subcycle_pls_cnt <= 8'd0; 86 | end 87 | 88 | end 89 | 90 | end 91 | 92 | assign wfg_subcore_subcycle_o = temp_subcycle & ~subcycle_dly; 93 | assign wfg_subcore_sync_o = temp_sync & ~sync_dly; 94 | assign active_o = en_i; 95 | assign wfg_subcore_start_o = en_i & ~en_i_dly; 96 | assign wfg_subcore_subcycle_cnt_o = subcycle_pls_cnt; 97 | 98 | endmodule 99 | `default_nettype wire 100 | -------------------------------------------------------------------------------- /design/wfg_subcore/rtl/wfg_subcore_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_subcore_top #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output wbs_ack_o, 18 | output [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // subcore synchronisation interface 21 | output wire wfg_subcore_sync_o, // O; Sync signal 22 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal 23 | output wire wfg_subcore_start_o, // O; Indicate start 24 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter 25 | output wire active_o // O; Active indication signal 26 | ); 27 | // Registers 28 | //marker_template_start 29 | //data: ../data/wfg_subcore_reg.json 30 | //template: wishbone/instantiate_top.template 31 | //marker_template_code 32 | 33 | logic [23: 8] cfg_subcycle_q; // CFG.SUBCYCLE register output 34 | logic [ 7: 0] cfg_sync_q; // CFG.SYNC register output 35 | logic ctrl_en_q; // CTRL.EN register output 36 | 37 | //marker_template_end 38 | 39 | wfg_subcore_wishbone_reg wfg_subcore_wishbone_reg ( 40 | .wb_clk_i (wb_clk_i), 41 | .wb_rst_i (wb_rst_i), 42 | .wbs_stb_i(wbs_stb_i), 43 | .wbs_cyc_i(wbs_cyc_i), 44 | .wbs_we_i (wbs_we_i), 45 | .wbs_sel_i(wbs_sel_i), 46 | .wbs_dat_i(wbs_dat_i), 47 | .wbs_adr_i(wbs_adr_i), 48 | .wbs_ack_o(wbs_ack_o), 49 | .wbs_dat_o(wbs_dat_o), 50 | 51 | //marker_template_start 52 | //data: ../data/wfg_subcore_reg.json 53 | //template: wishbone/assign_to_module.template 54 | //marker_template_code 55 | 56 | .cfg_subcycle_q_o(cfg_subcycle_q), // CFG.SUBCYCLE register output 57 | .cfg_sync_q_o (cfg_sync_q), // CFG.SYNC register output 58 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output 59 | 60 | //marker_template_end 61 | ); 62 | 63 | wfg_subcore wfg_subcore ( 64 | .clk (wb_clk_i), // clock signal 65 | .rst_n(!wb_rst_i), // reset signal 66 | 67 | // Control 68 | .en_i(ctrl_en_q), // I; Enable signal 69 | 70 | // Configuration 71 | .wfg_sync_count_i (cfg_sync_q), // I; Sync counter threshold 72 | .wfg_subcycle_count_i(cfg_subcycle_q), // I: Subcycle counter threshold 73 | 74 | // Output 75 | .wfg_subcore_sync_o (wfg_subcore_sync_o), // O; Sync signal 76 | .wfg_subcore_subcycle_o (wfg_subcore_subcycle_o), // O; Subcycle signal 77 | .wfg_subcore_start_o (wfg_subcore_start_o), // O; Indicate start 78 | .wfg_subcore_subcycle_cnt_o(wfg_subcore_subcycle_cnt_o), // O; Subcycle pulse counter 79 | .active_o (active_o) // O; Active indication signal 80 | ); 81 | 82 | endmodule 83 | `default_nettype wire 84 | -------------------------------------------------------------------------------- /design/wfg_subcore/rtl/wfg_subcore_wishbone_reg.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `default_nettype none 5 | module wfg_subcore_wishbone_reg #( 6 | parameter int BUSW = 32 7 | ) ( 8 | // Wishbone Slave ports 9 | input wb_clk_i, 10 | input wb_rst_i, 11 | input wbs_stb_i, 12 | input wbs_cyc_i, 13 | input wbs_we_i, 14 | input [(BUSW/8-1):0] wbs_sel_i, 15 | input [ (BUSW-1):0] wbs_dat_i, 16 | input [ (BUSW-1):0] wbs_adr_i, 17 | output logic wbs_ack_o, 18 | output logic [ (BUSW-1):0] wbs_dat_o, 19 | 20 | // Registers 21 | //marker_template_start 22 | //data: ../data/wfg_subcore_reg.json 23 | //template: wishbone/register_interface.template 24 | //marker_template_code 25 | 26 | output logic [23:8] cfg_subcycle_q_o, // CFG.SUBCYCLE register output 27 | output logic [ 7:0] cfg_sync_q_o, // CFG.SYNC register output 28 | output logic ctrl_en_q_o // CTRL.EN register output 29 | 30 | //marker_template_end 31 | ); 32 | 33 | //marker_template_start 34 | //data: ../data/wfg_subcore_reg.json 35 | //template: wishbone/instantiate_registers.template 36 | //marker_template_code 37 | 38 | logic [23: 8] cfg_subcycle_ff; // CFG.SUBCYCLE FF 39 | logic [ 7: 0] cfg_sync_ff; // CFG.SYNC FF 40 | logic ctrl_en_ff; // CTRL.EN FF 41 | 42 | //marker_template_end 43 | 44 | // Wishbone write to slave 45 | always_ff @(posedge wb_clk_i) begin 46 | if (wb_rst_i) begin 47 | //marker_template_start 48 | //data: ../data/wfg_subcore_reg.json 49 | //template: wishbone/reset_registers.template 50 | //marker_template_code 51 | 52 | cfg_subcycle_ff <= 0; 53 | cfg_sync_ff <= 0; 54 | ctrl_en_ff <= 1'b0; 55 | 56 | //marker_template_end 57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin 58 | case (wbs_adr_i) 59 | //marker_template_start 60 | //data: ../data/wfg_subcore_reg.json 61 | //template: wishbone/assign_to_registers.template 62 | //marker_template_code 63 | 64 | 4'h4: begin 65 | cfg_subcycle_ff <= wbs_dat_i[23:8]; 66 | cfg_sync_ff <= wbs_dat_i[7:0]; 67 | end 68 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0]; 69 | 70 | //marker_template_end 71 | default: begin 72 | end 73 | endcase 74 | end 75 | end 76 | 77 | // Wishbone read from slave 78 | always_ff @(posedge wb_clk_i) begin 79 | if (wb_rst_i) begin 80 | wbs_dat_o <= '0; 81 | end else begin 82 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin 83 | wbs_dat_o <= '0; // default value 84 | case (wbs_adr_i) 85 | //marker_template_start 86 | //data: ../data/wfg_subcore_reg.json 87 | //template: wishbone/assign_from_registers.template 88 | //marker_template_code 89 | 90 | 4'h4: begin 91 | wbs_dat_o[23:8] <= cfg_subcycle_ff; 92 | wbs_dat_o[7:0] <= cfg_sync_ff; 93 | end 94 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff; 95 | 96 | //marker_template_end 97 | default: wbs_dat_o <= 'X; 98 | endcase 99 | end 100 | end 101 | end 102 | 103 | // Acknowledgement 104 | always_ff @(posedge wb_clk_i) begin 105 | if (wb_rst_i) wbs_ack_o <= 1'b0; 106 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o; 107 | end 108 | 109 | //marker_template_start 110 | //data: ../data/wfg_subcore_reg.json 111 | //template: wishbone/assign_outputs.template 112 | //marker_template_code 113 | 114 | assign cfg_subcycle_q_o = cfg_subcycle_ff; 115 | assign cfg_sync_q_o = cfg_sync_ff; 116 | assign ctrl_en_q_o = ctrl_en_ff; 117 | 118 | //marker_template_end 119 | endmodule 120 | `default_nettype wire 121 | -------------------------------------------------------------------------------- /design/wfg_subcore/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | 5 | # defaults 6 | SIM ?= icarus 7 | TOPLEVEL_LANG ?= verilog 8 | 9 | VERILOG_SOURCES += $(SRC) 10 | 11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 12 | TOPLEVEL = wfg_subcore_tb 13 | 14 | # MODULE is the basename of the Python test file 15 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 16 | MODULE = test_wfg_subcore 17 | 18 | # include cocotb's make rules to take care of the simulator setup 19 | include $(shell cocotb-config --makefiles)/Makefile.sim 20 | -------------------------------------------------------------------------------- /design/wfg_subcore/sim/view.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI 3 | [*] Fri May 13 06:43:30 2022 4 | [*] 5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/wfg_core_tb.vcd" 6 | [dumpfile_mtime] "Fri May 13 06:30:51 2022" 7 | [dumpfile_size] 1052480 8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/view.gtkw" 9 | [timestart] 0 10 | [size] 1776 1043 11 | [pos] -1 -1 12 | *-20.000000 810000 -1 -1 -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] wfg_core_tb. 14 | [treeopen] wfg_core_tb.wfg_core_top. 15 | [sst_width] 227 16 | [signals_width] 488 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 410 19 | @200 20 | -Wishbone 21 | @28 22 | wfg_core_tb.io_wbs_clk 23 | @22 24 | wfg_core_tb.io_wbs_adr[31:0] 25 | wfg_core_tb.io_wbs_datrd[31:0] 26 | wfg_core_tb.io_wbs_datwr[31:0] 27 | @28 28 | wfg_core_tb.io_wbs_ack 29 | wfg_core_tb.io_wbs_cyc 30 | wfg_core_tb.io_wbs_rst 31 | wfg_core_tb.io_wbs_stb 32 | wfg_core_tb.io_wbs_we 33 | @200 34 | -Core 35 | @28 36 | wfg_core_tb.wfg_pat_start_o 37 | wfg_core_tb.wfg_pat_subcycle_o 38 | wfg_core_tb.wfg_pat_sync_o 39 | @22 40 | wfg_core_tb.wfg_pat_subcycle_cnt_o[7:0] 41 | @28 42 | wfg_core_tb.active_o 43 | @200 44 | -Internals 45 | @28 46 | wfg_core_tb.wfg_core_top.wfg_core.active_o 47 | wfg_core_tb.wfg_core_top.wfg_core.clk 48 | wfg_core_tb.wfg_core_top.wfg_core.en_i 49 | wfg_core_tb.wfg_core_top.wfg_core.en_i_dly 50 | wfg_core_tb.wfg_core_top.wfg_core.rst_n 51 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_start_o 52 | @22 53 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_count_ff[15:0] 54 | @28 55 | wfg_core_tb.wfg_core_top.wfg_core.temp_subcycle_ff 56 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_dly 57 | @22 58 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_pls_cnt[7:0] 59 | wfg_core_tb.wfg_core_top.wfg_core.sync_count_ff[7:0] 60 | @28 61 | wfg_core_tb.wfg_core_top.wfg_core.temp_sync_ff 62 | wfg_core_tb.wfg_core_top.wfg_core.sync_dly 63 | @22 64 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_cnt_o[7:0] 65 | @28 66 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_o 67 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_sync_o 68 | @22 69 | wfg_core_tb.wfg_core_top.wfg_core.wfg_subcycle_count_i[15:0] 70 | wfg_core_tb.wfg_core_top.wfg_core.wfg_sync_count_i[7:0] 71 | [pattern_trace] 1 72 | [pattern_trace] 0 73 | -------------------------------------------------------------------------------- /design/wfg_subcore/testbench/test_wfg_core.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import random 5 | import cocotb 6 | from cocotb.clock import Clock 7 | from cocotb.regression import TestFactory 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles 9 | from cocotbext.wishbone.driver import WishboneMaster 10 | from cocotbext.wishbone.driver import WBOp 11 | 12 | CLK_PER_SYNC = 300 13 | SYSCLK = 100000000 14 | DATA_CNT = 10 15 | 16 | short_per = Timer(100, units="ns") 17 | long_time = Timer(100, units="us") 18 | 19 | async def set_register(dut, wbs, address, data): 20 | dut._log.info(f"Set register {address} : {data}") 21 | 22 | wbRes = await wbs.send_cycle([WBOp(address, data)]) 23 | 24 | rvalues = [wb.datrd for wb in wbRes] 25 | dut._log.info(f"Returned values : {rvalues}") 26 | 27 | async def configure(dut, wbs, en, sync_count, subcycle_count): 28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8)) 29 | await set_register(dut, wbs, 0x0, en) # Enable core 30 | 31 | @cocotb.coroutine 32 | async def core_test(dut, en, sync_count, subcycle_count): 33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}") 34 | 35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start()) 36 | 37 | dut._log.info("Initialize and reset model") 38 | 39 | # Start reset 40 | dut.io_wbs_rst.value = 1 41 | await Timer(100, units='ns') 42 | 43 | # Stop reset 44 | dut.io_wbs_rst.value = 0 45 | 46 | # Wishbone Master 47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 48 | width=32, # size of data bus 49 | timeout=10) # in clock cycle number 50 | 51 | # Setup core 52 | await configure(dut, wbs, en, sync_count, subcycle_count) 53 | 54 | #await short_per 55 | #await short_per 56 | 57 | sync_pulse_count = 0 58 | clk_count = 0 59 | 60 | await FallingEdge(dut.wfg_core_sync_o) 61 | 62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3): 63 | await ClockCycles(dut.io_wbs_clk, 1) 64 | clk_count += 1 65 | 66 | if dut.wfg_core_sync_o == 1: 67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2) 68 | break 69 | 70 | if (dut.wfg_core_subcycle_o == 1): 71 | sync_pulse_count += 1 72 | clk_count = 0 73 | 74 | length = 2 75 | 76 | sync_array = [] 77 | 78 | for i in range(length): 79 | n = random.randint(1,2**7) 80 | sync_array.append(n) 81 | 82 | subcycle_array = [] 83 | 84 | for i in range(length): 85 | n = random.randint(1,2**7) 86 | subcycle_array.append(n) 87 | 88 | 89 | factory = TestFactory(core_test) 90 | factory.add_option("en", [1]) 91 | factory.add_option("sync_count", sync_array) 92 | factory.add_option("subcycle_count", subcycle_array) 93 | 94 | factory.generate_tests() 95 | -------------------------------------------------------------------------------- /design/wfg_subcore/testbench/test_wfg_subcore.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 semify 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import random 5 | import cocotb 6 | from cocotb.clock import Clock 7 | from cocotb.regression import TestFactory 8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles 9 | from cocotbext.wishbone.driver import WishboneMaster 10 | from cocotbext.wishbone.driver import WBOp 11 | 12 | CLK_PER_SYNC = 300 13 | SYSCLK = 100000000 14 | DATA_CNT = 10 15 | 16 | short_per = Timer(100, units="ns") 17 | long_time = Timer(100, units="us") 18 | 19 | async def set_register(dut, wbs, address, data): 20 | dut._log.info(f"Set register {address} : {data}") 21 | 22 | wbRes = await wbs.send_cycle([WBOp(address, data)]) 23 | 24 | rvalues = [wb.datrd for wb in wbRes] 25 | dut._log.info(f"Returned values : {rvalues}") 26 | 27 | async def configure(dut, wbs, en, sync_count, subcycle_count): 28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8)) 29 | await set_register(dut, wbs, 0x0, en) # Enable subcore 30 | 31 | @cocotb.coroutine 32 | async def subcore_test(dut, en, sync_count, subcycle_count): 33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}") 34 | 35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start()) 36 | 37 | dut._log.info("Initialize and reset model") 38 | 39 | # Start reset 40 | dut.io_wbs_rst.value = 1 41 | await Timer(100, units='ns') 42 | 43 | # Stop reset 44 | dut.io_wbs_rst.value = 0 45 | 46 | # Wishbone Master 47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk, 48 | width=32, # size of data bus 49 | timeout=10) # in clock cycle number 50 | 51 | # Setup subcore 52 | await configure(dut, wbs, en, sync_count, subcycle_count) 53 | 54 | #await short_per 55 | #await short_per 56 | 57 | sync_pulse_count = 0 58 | clk_count = 0 59 | 60 | await FallingEdge(dut.wfg_subcore_sync_o) 61 | 62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3): 63 | await ClockCycles(dut.io_wbs_clk, 1) 64 | clk_count += 1 65 | 66 | if dut.wfg_subcore_sync_o == 1: 67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2) 68 | break 69 | 70 | if (dut.wfg_subcore_subcycle_o == 1): 71 | sync_pulse_count += 1 72 | clk_count = 0 73 | 74 | length = 2 75 | 76 | sync_array = [] 77 | 78 | for i in range(length): 79 | n = random.randint(1,2**7) 80 | sync_array.append(n) 81 | 82 | subcycle_array = [] 83 | 84 | for i in range(length): 85 | n = random.randint(1,2**7) 86 | subcycle_array.append(n) 87 | 88 | 89 | factory = TestFactory(subcore_test) 90 | factory.add_option("en", [1]) 91 | factory.add_option("sync_count", sync_array) 92 | factory.add_option("subcycle_count", subcycle_array) 93 | 94 | factory.generate_tests() 95 | -------------------------------------------------------------------------------- /design/wfg_subcore/testbench/wfg_subcore_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_subcore_tb #( 13 | parameter int BUSW = 32 14 | ) ( 15 | // Wishbone interface signals 16 | input io_wbs_clk, 17 | input io_wbs_rst, 18 | input [(BUSW-1):0] io_wbs_adr, 19 | input [(BUSW-1):0] io_wbs_datwr, 20 | output [(BUSW-1):0] io_wbs_datrd, 21 | input io_wbs_we, 22 | input io_wbs_stb, 23 | output io_wbs_ack, 24 | input io_wbs_cyc, 25 | 26 | // subcore synchronisation interface 27 | output wire wfg_subcore_sync_o, // O; Sync signal 28 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal 29 | output wire wfg_subcore_start_o, // O; Indicate start 30 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter 31 | output wire active_o // O; Active indication signal 32 | ); 33 | 34 | wfg_subcore_top wfg_subcore_top ( 35 | .wb_clk_i (io_wbs_clk), 36 | .wb_rst_i (io_wbs_rst), 37 | .wbs_stb_i(io_wbs_stb), 38 | .wbs_cyc_i(io_wbs_cyc), 39 | .wbs_we_i (io_wbs_we), 40 | .wbs_sel_i(4'b1111), 41 | .wbs_dat_i(io_wbs_datwr), 42 | .wbs_adr_i(io_wbs_adr), 43 | .wbs_ack_o(io_wbs_ack), 44 | .wbs_dat_o(io_wbs_datrd), 45 | 46 | // subcore synchronisation interface 47 | .wfg_subcore_sync_o(wfg_subcore_sync_o), 48 | .wfg_subcore_subcycle_o(wfg_subcore_subcycle_o), 49 | .wfg_subcore_start_o(wfg_subcore_start_o), 50 | .wfg_subcore_subcycle_cnt_o(wfg_subcore_subcycle_cnt_o), 51 | .active_o(active_o) 52 | ); 53 | 54 | // Dump waves 55 | `ifndef VERILATOR 56 | initial begin 57 | $dumpfile("wfg_subcore_tb.vcd"); 58 | $dumpvars(0, wfg_subcore_tb); 59 | end 60 | `endif 61 | 62 | endmodule 63 | -------------------------------------------------------------------------------- /design/wfg_top/sim/Makefile: -------------------------------------------------------------------------------- 1 | # source files 2 | SRC := $(wildcard ../rtl/*.sv) 3 | SRC += $(wildcard ../testbench/*.sv) 4 | SRC += $(wildcard ../../wfg_core/rtl/*.sv) 5 | SRC += $(wildcard ../../wfg_subcore/rtl/*.sv) 6 | SRC += $(wildcard ../../wfg_interconnect/rtl/*.sv) 7 | SRC += $(wildcard ../../wfg_stim_sine/rtl/*.sv) 8 | SRC += $(wildcard ../../wfg_stim_mem/rtl/*.sv) 9 | SRC += $(wildcard ../../wfg_drive_spi/rtl/*.sv) 10 | SRC += $(wildcard ../../wfg_drive_pat/rtl/*.sv) 11 | 12 | # defaults 13 | SIM ?= icarus 14 | TOPLEVEL_LANG ?= verilog 15 | 16 | VERILOG_SOURCES += $(SRC) 17 | 18 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file 19 | TOPLEVEL = wfg_top_tb 20 | 21 | # MODULE is the basename of the Python test file 22 | export PYTHONPATH := $(PYTHONPATH):../testbench/ 23 | MODULE = test_wfg_top 24 | 25 | # include cocotb's make rules to take care of the simulator setup 26 | include $(shell cocotb-config --makefiles)/Makefile.sim 27 | -------------------------------------------------------------------------------- /design/wfg_top/sim/memory.hex: -------------------------------------------------------------------------------- 1 | 00000000 2 | 00000001 3 | 00000002 4 | 00000003 5 | 00000004 6 | 00000005 7 | 00000006 8 | 00000007 9 | 00000008 10 | 00000009 11 | 0000000A 12 | 0000000B 13 | 0000000C 14 | 0000000D 15 | 0000000E 16 | 0000000F 17 | 00000010 18 | 00000011 19 | 00000012 20 | 00000013 21 | 00000014 22 | 00000015 23 | 00000016 24 | 00000017 25 | 00000018 26 | 00000019 27 | 0000001A 28 | 0000001B 29 | 0000001C 30 | 0000001D 31 | 0000001E 32 | 0000001F 33 | -------------------------------------------------------------------------------- /design/wfg_top/sim/output_inc=1280_gain=16384_off=0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_top/sim/output_inc=1280_gain=16384_off=0.png -------------------------------------------------------------------------------- /design/wfg_top/sim/output_inc=4096_gain=16384_off=0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_top/sim/output_inc=4096_gain=16384_off=0.png -------------------------------------------------------------------------------- /design/wfg_top/sim/view.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI 3 | [*] Mon May 30 07:45:21 2022 4 | [*] 5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_top/sim/wfg_top_tb.vcd" 6 | [dumpfile_mtime] "Mon May 30 07:43:16 2022" 7 | [dumpfile_size] 992395 8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_top/sim/view.gtkw" 9 | [timestart] 0 10 | [size] 1920 1043 11 | [pos] -1971 -83 12 | *-24.000000 -1 -1 -1 -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] wfg_top_tb. 14 | [treeopen] wfg_top_tb.wfg_top. 15 | [treeopen] wfg_top_tb.wfg_top.wfg_stim_sine_top. 16 | [sst_width] 284 17 | [signals_width] 206 18 | [sst_expanded] 1 19 | [sst_vpaned_height] 295 20 | @28 21 | wfg_top_tb.io_wbs_ack 22 | @22 23 | wfg_top_tb.io_wbs_adr[31:0] 24 | @28 25 | wfg_top_tb.io_wbs_clk 26 | wfg_top_tb.io_wbs_cyc 27 | @22 28 | wfg_top_tb.io_wbs_datrd[31:0] 29 | wfg_top_tb.io_wbs_datwr[31:0] 30 | @28 31 | wfg_top_tb.io_wbs_rst 32 | wfg_top_tb.io_wbs_stb 33 | wfg_top_tb.io_wbs_we 34 | wfg_top_tb.wfg_drive_spi_cs_no 35 | wfg_top_tb.wfg_drive_spi_sclk_o 36 | wfg_top_tb.wfg_drive_spi_sdi_i 37 | wfg_top_tb.wfg_drive_spi_sdo_o 38 | @200 39 | -Stim Sine 40 | @29 41 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.clk 42 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.ctrl_en_q_i 43 | @421 44 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.cur_state 45 | @23 46 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.gain_val_q_i[15:0] 47 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.inc_val_q_i[15:0] 48 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.increment[15:0] 49 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.iteration[31:0] 50 | @421 51 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.next_state 52 | @23 53 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.offset_val_q_i[17:0] 54 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.overflow_chk[17:0] 55 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.phase_in[15:0] 56 | @29 57 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.quadrant[1:0] 58 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.rst_n 59 | @23 60 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.sin_17[16:0] 61 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.sin_18[17:0] 62 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.temp[34:0] 63 | @29 64 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.valid 65 | @23 66 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tdata_o[17:0] 67 | @29 68 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tready_i 69 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tvalid_o 70 | @23 71 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.x[16:0] 72 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.y[16:0] 73 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.z[16:0] 74 | [pattern_trace] 1 75 | [pattern_trace] 0 76 | -------------------------------------------------------------------------------- /design/wfg_top/testbench/wfg_top_tb.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | `timescale 1ns / 1ps 5 | 6 | `ifdef VERILATOR // make parameter readable from VPI 7 | `define VL_RD /*verilator public_flat_rd*/ 8 | `else 9 | `define VL_RD 10 | `endif 11 | 12 | module wfg_top_tb #( 13 | parameter int BUSW = 32 14 | ) ( 15 | // Wishbone interface signals 16 | input io_wbs_clk, 17 | input io_wbs_rst, 18 | input [(BUSW-1):0] io_wbs_adr, 19 | input [(BUSW-1):0] io_wbs_datwr, 20 | output [(BUSW-1):0] io_wbs_datrd, 21 | input io_wbs_we, 22 | input io_wbs_stb, 23 | output io_wbs_ack, 24 | input io_wbs_cyc, 25 | 26 | input logic wfg_drive_spi_sdi_i, // for cocotb 27 | output logic wfg_drive_spi_sclk_o, 28 | output logic wfg_drive_spi_cs_no, 29 | output logic wfg_drive_spi_sdo_o, 30 | 31 | output logic [31:0] wfg_drive_pat_dout_o 32 | ); 33 | 34 | logic csb1; 35 | logic [9:0 ] addr1; 36 | logic [31:0] dout1; 37 | 38 | localparam MEM_SIZE = 2 ** 10; 39 | 40 | logic [31:0] mem[MEM_SIZE]; 41 | 42 | wfg_top wfg_top ( 43 | .io_wbs_clk(io_wbs_clk), 44 | .io_wbs_rst(io_wbs_rst), 45 | .io_wbs_adr(io_wbs_adr), 46 | .io_wbs_datwr(io_wbs_datwr), 47 | .io_wbs_datrd(io_wbs_datrd), 48 | .io_wbs_we(io_wbs_we), 49 | .io_wbs_stb(io_wbs_stb), 50 | .io_wbs_ack(io_wbs_ack), 51 | .io_wbs_cyc(io_wbs_cyc), 52 | 53 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o), 54 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no), 55 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o), 56 | 57 | .wfg_drive_pat_dout_o(wfg_drive_pat_dout_o), 58 | 59 | .csb1 (csb1), 60 | .addr1(addr1), 61 | .dout1(dout1) 62 | ); 63 | 64 | initial begin 65 | $readmemh("memory.hex", mem); 66 | end 67 | 68 | always_ff @(negedge io_wbs_clk) begin 69 | if (!csb1) dout1 <= mem[addr1]; 70 | end 71 | 72 | // Dump waves 73 | `ifndef VERILATOR 74 | initial begin 75 | $dumpfile("wfg_top_tb.vcd"); 76 | $dumpvars(0, wfg_top_tb); 77 | end 78 | `endif 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /fpga/ulx3s_barebones/ulx3s_top.sv: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 semify 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module ulx3s_top ( 5 | input clk_25mhz, 6 | 7 | input [6:0] btn, 8 | output logic [7:0] led 9 | ); 10 | parameter int BUSW = 32; 11 | 12 | // Wishbone interface signals 13 | logic io_wbs_clk; 14 | logic io_wbs_rst; 15 | logic [(BUSW-1):0] io_wbs_adr; 16 | logic [(BUSW-1):0] io_wbs_datwr; 17 | logic [(BUSW-1):0] io_wbs_datrd; 18 | logic io_wbs_we; 19 | logic io_wbs_stb; 20 | logic io_wbs_ack; 21 | logic io_wbs_cyc; 22 | 23 | assign io_wbs_clk = clk_25mhz; 24 | assign io_wbs_rst = btn[0]; 25 | assign io_wbs_adr = {btn[5], btn[4], btn[3], btn[2], btn[1], btn[0]}; 26 | assign io_wbs_datwr = btn[2]; 27 | assign io_wbs_we = btn[3]; 28 | assign io_wbs_stb = btn[4]; 29 | assign io_wbs_cyc = btn[5]; 30 | 31 | assign led[3] = io_wbs_ack; 32 | assign led[4] = | io_wbs_datrd; 33 | 34 | logic wfg_drive_spi_sclk_o; 35 | logic wfg_drive_spi_cs_no; 36 | logic wfg_drive_spi_sdo_o; 37 | logic wfg_drive_spi_sdo_en_o; 38 | 39 | assign led[0] = wfg_drive_spi_sclk_o; 40 | assign led[1] = wfg_drive_spi_cs_no; 41 | assign led[2] = wfg_drive_spi_sdo_o; 42 | 43 | logic [31:0] wfg_drive_pat_dout_o; 44 | 45 | assign led[5] = | wfg_drive_pat_dout_o[7:0]; 46 | 47 | // Memory interface 48 | logic csb1; 49 | logic [ 9:0] addr1; 50 | logic [31:0] dout1; 51 | 52 | localparam MEM_SIZE = 2 ** 10; 53 | 54 | logic [31:0] mem[MEM_SIZE]; 55 | 56 | always_ff @(negedge io_wbs_clk) begin 57 | if (!csb1) dout1 <= mem[addr1]; 58 | end 59 | 60 | (* keep *) wfg_top wfg_top ( 61 | .io_wbs_clk(io_wbs_clk), 62 | .io_wbs_rst(io_wbs_rst), 63 | .io_wbs_adr(io_wbs_adr), 64 | .io_wbs_datwr(io_wbs_datwr), 65 | .io_wbs_datrd(io_wbs_datrd), 66 | .io_wbs_we(io_wbs_we), 67 | .io_wbs_stb(io_wbs_stb), 68 | .io_wbs_ack(io_wbs_ack), 69 | .io_wbs_cyc(io_wbs_cyc), 70 | 71 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o), 72 | .wfg_drive_spi_cs_no(wfg_drive_spi_cs_no), 73 | .wfg_drive_spi_sdo_o(wfg_drive_spi_sdo_o), 74 | 75 | .wfg_drive_pat_dout_o(wfg_drive_pat_dout_o), 76 | 77 | .csb1(csb1), 78 | .addr1(addr1), 79 | .dout1(dout1) 80 | ); 81 | 82 | endmodule 83 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/Makefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | #CPU_TYPE = vexriscv 4 | CPU_TYPE = femtorv 5 | 6 | firmware/firmware_sim.bin: 7 | cd firmware; make -f sim.make clean; make -f sim.make 8 | 9 | firmware/firmware_build.bin: 10 | cd firmware; make -f build.make clean; make -f build.make 11 | 12 | sim-prebuild: 13 | python3 ulx3s_soc.py --sim-debug --gtkwave-savefile --trace --trace-fst --with-wfg --simulate --cpu-type $(CPU_TYPE) 14 | 15 | sim: firmware/firmware_sim.bin 16 | python3 ulx3s_soc.py --rom-init=firmware/firmware_sim.bin --sim-debug --gtkwave-savefile --trace --trace-fst --with-wfg --simulate --cpu-type $(CPU_TYPE) 17 | 18 | build-prebuild: 19 | python3 ulx3s_soc.py --with-led-chaser --with-wfg --build --cpu-type $(CPU_TYPE) 20 | 21 | build: firmware/firmware_build.bin 22 | python3 ulx3s_soc.py --rom-init=firmware/firmware_build.bin --with-led-chaser --with-wfg --build --cpu-type $(CPU_TYPE) 23 | 24 | upload: build 25 | openFPGALoader --board=ulx3s build/radiona_ulx3s/gateware/radiona_ulx3s.bit 26 | 27 | only-upload: 28 | openFPGALoader --board=ulx3s build/radiona_ulx3s/gateware/radiona_ulx3s.bit 29 | 30 | view: 31 | gtkwave build/sim/gateware/sim.fst --save build/sim/gateware/sim.gtkw 32 | 33 | .PHONY: sim build upload only-upload view firmware/firmware_build.bin firmware/firmware_sim.bin 34 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/analyzer.csv: -------------------------------------------------------------------------------- 1 | config,None,data_width,204 2 | config,None,depth,512 3 | config,None,samplerate,1000000000000 4 | signal,0,ibus_stb,1 5 | signal,0,ibus_cyc,1 6 | signal,0,ibus_adr,30 7 | signal,0,ibus_we,1 8 | signal,0,ibus_ack,1 9 | signal,0,ibus_sel,4 10 | signal,0,ibus_dat_w,32 11 | signal,0,ibus_dat_r,32 12 | signal,0,dbus_stb,1 13 | signal,0,dbus_cyc,1 14 | signal,0,dbus_adr,30 15 | signal,0,dbus_we,1 16 | signal,0,dbus_ack,1 17 | signal,0,dbus_sel,4 18 | signal,0,dbus_dat_w,32 19 | signal,0,dbus_dat_r,32 20 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/csr.csv: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------------------- 2 | # Auto-generated by LiteX (a977adf5) on 2022-07-18 10:34:45 3 | #-------------------------------------------------------------------------------- 4 | csr_base,ctrl,0x82000000,, 5 | csr_base,identifier_mem,0x82000800,, 6 | csr_base,leds,0x82001000,, 7 | csr_base,timer0,0x82001800,, 8 | csr_base,uart,0x82002000,, 9 | csr_register,ctrl_reset,0x82000000,1,rw 10 | csr_register,ctrl_scratch,0x82000004,1,rw 11 | csr_register,ctrl_bus_errors,0x82000008,1,ro 12 | csr_register,leds_out,0x82001000,1,rw 13 | csr_register,timer0_load,0x82001800,1,rw 14 | csr_register,timer0_reload,0x82001804,1,rw 15 | csr_register,timer0_en,0x82001808,1,rw 16 | csr_register,timer0_update_value,0x8200180c,1,rw 17 | csr_register,timer0_value,0x82001810,1,ro 18 | csr_register,timer0_ev_status,0x82001814,1,ro 19 | csr_register,timer0_ev_pending,0x82001818,1,rw 20 | csr_register,timer0_ev_enable,0x8200181c,1,rw 21 | csr_register,uart_rxtx,0x82002000,1,rw 22 | csr_register,uart_txfull,0x82002004,1,ro 23 | csr_register,uart_rxempty,0x82002008,1,ro 24 | csr_register,uart_ev_status,0x8200200c,1,ro 25 | csr_register,uart_ev_pending,0x82002010,1,rw 26 | csr_register,uart_ev_enable,0x82002014,1,rw 27 | csr_register,uart_txempty,0x82002018,1,ro 28 | csr_register,uart_rxfull,0x8200201c,1,ro 29 | constant,config_clock_frequency,50000000,, 30 | constant,config_cpu_reset_addr,0,, 31 | constant,config_cpu_type_femtorv,None,, 32 | constant,config_cpu_variant_standard,None,, 33 | constant,config_cpu_human_name,femtorv-standard,, 34 | constant,config_cpu_nop,nop,, 35 | constant,config_rom_init,1,, 36 | constant,uart_polling,None,, 37 | constant,config_csr_data_width,32,, 38 | constant,config_csr_alignment,32,, 39 | constant,config_bus_standard,wishbone,, 40 | constant,config_bus_data_width,32,, 41 | constant,config_bus_address_width,32,, 42 | constant,config_bus_bursting,0,, 43 | memory_region,rom,0x00000000,131072,cached 44 | memory_region,sram,0x01000000,8192,cached 45 | memory_region,wfg,0x30000000,1048576,cached 46 | memory_region,csr,0x82000000,65536,io 47 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/build.make: -------------------------------------------------------------------------------- 1 | BUILD_DIR?=../build/radiona_ulx3s/ 2 | 3 | include $(BUILD_DIR)/software/include/generated/variables.mak 4 | include $(SOC_DIRECTORY)/software/common.mak 5 | 6 | OBJECTS = sim_debug.o sim.o wfg.o donut.o crt0.o main.o 7 | 8 | all: firmware_build.bin 9 | 10 | %.bin: %.elf 11 | $(OBJCOPY) -O binary $< $@ 12 | chmod -x $@ 13 | 14 | vpath %.a $(PACKAGES:%=../%) 15 | 16 | firmware_build.elf: $(OBJECTS) 17 | $(CC) $(LDFLAGS) -T linker.ld -N -o $@ \ 18 | $(OBJECTS) \ 19 | $(PACKAGES:%=-L$(BUILD_DIR)/software/%) \ 20 | -Wl,--gc-sections \ 21 | $(LIBS:lib%=-l%) 22 | chmod -x $@ 23 | 24 | # pull in dependency info for *existing* .o files 25 | -include $(OBJECTS:.o=.d) 26 | 27 | donut.o: CFLAGS += -w 28 | 29 | VPATH = $(BIOS_DIRECTORY):$(BIOS_DIRECTORY)/cmds:$(CPU_DIRECTORY) 30 | 31 | %.o: %.cpp 32 | $(compilexx) 33 | 34 | %.o: %.c 35 | $(compile) 36 | 37 | %.o: %.S 38 | $(assemble) 39 | 40 | clean: 41 | $(RM) $(OBJECTS) firmware_build.elf firmware_build.bin .*~ *~ *.d 42 | 43 | .PHONY: all clean 44 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/donut.c: -------------------------------------------------------------------------------- 1 | // The donut code with fixed-point arithmetic; no sines, cosines, square roots, or anything. 2 | // a1k0n 2020 3 | // Code from: https://gist.github.com/a1k0n/80f48aa8911fffd805316b8ba8f48e83 4 | // For more info: 5 | // - https://www.a1k0n.net/2011/07/20/donut-math.html 6 | // - https://www.youtube.com/watch?v=DEqXNfs_HhY 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define R(mul,shift,x,y) \ 15 | _=x; \ 16 | x -= mul*y>>shift; \ 17 | y += mul*_>>shift; \ 18 | _ = 3145728-x*x-y*y>>11; \ 19 | x = x*_>>10; \ 20 | y = y*_>>10; 21 | 22 | signed char b[1760], z[1760]; 23 | 24 | void donut(void); 25 | void donut(void) { 26 | int sA=1024,cA=0,sB=1024,cB=0,_; 27 | for (;;) { 28 | memset(b, 32, 1760); // text buffer 29 | memset(z, 127, 1760); // z buffer 30 | int sj=0, cj=1024; 31 | for (int j = 0; j < 90; j++) { 32 | int si = 0, ci = 1024; // sine and cosine of angle i 33 | for (int i = 0; i < 324; i++) { 34 | int R1 = 1, R2 = 2048, K2 = 5120*1024; 35 | 36 | int x0 = R1*cj + R2, 37 | x1 = ci*x0 >> 10, 38 | x2 = cA*sj >> 10, 39 | x3 = si*x0 >> 10, 40 | x4 = R1*x2 - (sA*x3 >> 10), 41 | x5 = sA*sj >> 10, 42 | x6 = K2 + R1*1024*x5 + cA*x3, 43 | x7 = cj*si >> 10, 44 | x = 40 + 30*(cB*x1 - sB*x4)/x6, 45 | y = 12 + 15*(cB*x4 + sB*x1)/x6, 46 | N = (-cA*x7 - cB*((-sA*x7>>10) + x2) - ci*(cj*sB >> 10) >> 10) - x5 >> 7; 47 | 48 | int o = x + 80 * y; 49 | signed char zz = (x6-K2)>>15; 50 | if (22 > y && y > 0 && x > 0 && 80 > x && zz < z[o]) { 51 | z[o] = zz; 52 | b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0]; 53 | } 54 | R(5, 8, ci, si) // rotate i 55 | } 56 | R(9, 7, cj, sj) // rotate j 57 | } 58 | for (int k = 0; 1761 > k; k++) 59 | putchar(k % 80 ? b[k] : 10); 60 | R(5, 7, cA, sA); 61 | R(5, 8, cB, sB); 62 | if (readchar_nonblock()) { 63 | getchar(); 64 | break; 65 | } 66 | fputs("\x1b[23A", stdout); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/firmware_build.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_build.bin -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/firmware_build.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_build.elf -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/firmware_sim.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_sim.bin -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/firmware_sim.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_sim.elf -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/linker.ld: -------------------------------------------------------------------------------- 1 | INCLUDE generated/output_format.ld 2 | ENTRY(_start) 3 | 4 | __DYNAMIC = 0; 5 | 6 | INCLUDE generated/regions.ld 7 | 8 | SECTIONS 9 | { 10 | .text : 11 | { 12 | _ftext = .; 13 | /* Make sure crt0 files come first, and they, and the isr */ 14 | /* don't get disposed of by greedy optimisation */ 15 | *crt0*(.text) 16 | KEEP(*crt0*(.text)) 17 | KEEP(*(.text.isr)) 18 | 19 | *(.text .stub .text.* .gnu.linkonce.t.*) 20 | _etext = .; 21 | } > rom 22 | 23 | .rodata : 24 | { 25 | . = ALIGN(8); 26 | _frodata = .; 27 | *(.rodata .rodata.* .gnu.linkonce.r.*) 28 | *(.rodata1) 29 | . = ALIGN(8); 30 | _erodata = .; 31 | } > rom 32 | 33 | .data : 34 | { 35 | . = ALIGN(8); 36 | _fdata = .; 37 | *(.data .data.* .gnu.linkonce.d.*) 38 | *(.data1) 39 | _gp = ALIGN(16); 40 | *(.sdata .sdata.* .gnu.linkonce.s.*) 41 | . = ALIGN(8); 42 | _edata = .; 43 | } > sram AT > rom 44 | 45 | .bss : 46 | { 47 | . = ALIGN(8); 48 | _fbss = .; 49 | *(.dynsbss) 50 | *(.sbss .sbss.* .gnu.linkonce.sb.*) 51 | *(.scommon) 52 | *(.dynbss) 53 | *(.bss .bss.* .gnu.linkonce.b.*) 54 | *(COMMON) 55 | . = ALIGN(8); 56 | _ebss = .; 57 | _end = .; 58 | } > sram 59 | } 60 | 61 | PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram)); 62 | 63 | PROVIDE(_fdata_rom = LOADADDR(.data)); 64 | PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data)); 65 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/main.c: -------------------------------------------------------------------------------- 1 | // This file is Copyright (c) 2020 Florent Kermarrec 2 | // License: BSD 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /*-----------------------------------------------------------------------*/ 15 | /* Uart */ 16 | /*-----------------------------------------------------------------------*/ 17 | 18 | static char *readstr(void) 19 | { 20 | char c[2]; 21 | static char s[64]; 22 | static int ptr = 0; 23 | 24 | if(readchar_nonblock()) { 25 | c[0] = getchar(); 26 | c[1] = 0; 27 | switch(c[0]) { 28 | case 0x7f: 29 | case 0x08: 30 | if(ptr > 0) { 31 | ptr--; 32 | fputs("\x08 \x08", stdout); 33 | } 34 | break; 35 | case 0x07: 36 | break; 37 | case '\r': 38 | case '\n': 39 | s[ptr] = 0x00; 40 | fputs("\n", stdout); 41 | ptr = 0; 42 | return s; 43 | default: 44 | if(ptr >= (sizeof(s) - 1)) 45 | break; 46 | fputs(c, stdout); 47 | s[ptr] = c[0]; 48 | ptr++; 49 | break; 50 | } 51 | } 52 | 53 | return NULL; 54 | } 55 | 56 | static char *get_token(char **str) 57 | { 58 | char *c, *d; 59 | 60 | c = (char *)strchr(*str, ' '); 61 | if(c == NULL) { 62 | d = *str; 63 | *str = *str+strlen(*str); 64 | return d; 65 | } 66 | *c = 0; 67 | d = *str; 68 | *str = c+1; 69 | return d; 70 | } 71 | 72 | static void prompt(void) 73 | { 74 | printf("\e[92;1msemify\e[0m> "); 75 | } 76 | 77 | /*-----------------------------------------------------------------------*/ 78 | /* Help */ 79 | /*-----------------------------------------------------------------------*/ 80 | 81 | static void help(void) 82 | { 83 | puts("\nMy first LiteX SoC built "__DATE__" "__TIME__"\n"); 84 | puts("Available commands:"); 85 | puts("help - Show this command"); 86 | puts("reboot - Reboot CPU"); 87 | puts("donut - Spinning Donut demo"); 88 | puts("mem_list - List available memory regions"); 89 | puts("wfg_init - Initialize the waveform generator"); 90 | #ifdef CSR_SIM_TRACE_BASE 91 | puts("trace - Toggle simulation tracing"); 92 | #endif 93 | #ifdef CSR_SIM_FINISH_BASE 94 | puts("finish - Finish simulation"); 95 | #endif 96 | #ifdef CSR_SIM_MARKER_BASE 97 | puts("mark - Set a debug simulation marker"); 98 | #endif 99 | } 100 | 101 | /*-----------------------------------------------------------------------*/ 102 | /* Commands */ 103 | /*-----------------------------------------------------------------------*/ 104 | 105 | static void reboot_cmd(void) 106 | { 107 | ctrl_reset_write(1); 108 | } 109 | 110 | extern void donut(void); 111 | 112 | static void donut_cmd(void) 113 | { 114 | printf("Donut demo...\n"); 115 | donut(); 116 | } 117 | 118 | static void mem_regions_cmd(void) 119 | { 120 | printf("Available memory regions:\n"); 121 | puts(MEM_REGIONS); 122 | } 123 | 124 | #ifdef WFG_BASE 125 | 126 | extern void wfg_init(void); 127 | extern void wfg_inc_cnt(void); 128 | extern void wfg_dec_cnt(void); 129 | 130 | static void wfg_init_cmd(void) 131 | { 132 | printf("Initializing wfg...\n"); 133 | wfg_init(); 134 | } 135 | 136 | #endif 137 | 138 | #ifdef CSR_SIM_TRACE_BASE 139 | extern void cmd_sim_trace_handler(int nb_params, char **params); 140 | #endif 141 | #ifdef CSR_SIM_FINISH_BASE 142 | extern void cmd_sim_finish_handler(int nb_params, char **params); 143 | #endif 144 | #ifdef CSR_SIM_MARKER_BASE 145 | extern void cmd_sim_mark_handler(int nb_params, char **params); 146 | #endif 147 | 148 | /*-----------------------------------------------------------------------*/ 149 | /* Console service / Main */ 150 | /*-----------------------------------------------------------------------*/ 151 | 152 | static void console_service(void) 153 | { 154 | char *str; 155 | char *token; 156 | 157 | str = readstr(); 158 | if(str == NULL) return; 159 | token = get_token(&str); 160 | if(strcmp(token, "help") == 0) 161 | help(); 162 | else if(strcmp(token, "reboot") == 0) 163 | reboot_cmd(); 164 | else if(strcmp(token, "donut") == 0) 165 | donut_cmd(); 166 | else if(strcmp(token, "mem_list") == 0) 167 | mem_regions_cmd(); 168 | #ifdef CSR_SIM_TRACE_BASE 169 | else if(strcmp(token, "trace") == 0) 170 | cmd_sim_trace_handler(0, 0); 171 | #endif 172 | #ifdef CSR_SIM_FINISH_BASE 173 | else if(strcmp(token, "finish") == 0) 174 | cmd_sim_finish_handler(0, 0); 175 | #endif 176 | #ifdef CSR_SIM_MARKER_BASE 177 | else if(strcmp(token, "mark") == 0) 178 | cmd_sim_mark_handler(0, 0); 179 | #endif 180 | #ifdef WFG_BASE 181 | else if(strcmp(token, "wfg_init") == 0) 182 | wfg_init_cmd(); 183 | else if(strcmp(token, "wfg_inc_cnt") == 0) 184 | wfg_inc_cnt(); 185 | else if(strcmp(token, "wfg_dec_cnt") == 0) 186 | wfg_dec_cnt(); 187 | #endif 188 | else 189 | printf("Unknown command. Type 'help' for help.\n"); 190 | prompt(); 191 | } 192 | 193 | int main(void) 194 | { 195 | #ifdef CONFIG_CPU_HAS_INTERRUPT 196 | irq_setmask(0); 197 | irq_setie(1); 198 | #endif 199 | uart_init(); 200 | 201 | help(); 202 | prompt(); 203 | 204 | while(1) { 205 | console_service(); 206 | } 207 | 208 | return 0; 209 | } 210 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/sim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim_debug.h" 4 | 5 | /** 6 | * Command "trace" 7 | * 8 | * Start/stop simulation trace dump. 9 | * 10 | */ 11 | #ifdef CSR_SIM_TRACE_BASE 12 | void cmd_sim_trace_handler(int nb_params, char **params) 13 | { 14 | sim_trace(!sim_trace_enable_read()); 15 | } 16 | #endif 17 | 18 | /** 19 | * Command "finish" 20 | * 21 | * Finish simulation. 22 | * 23 | */ 24 | #ifdef CSR_SIM_FINISH_BASE 25 | void cmd_sim_finish_handler(int nb_params, char **params) 26 | { 27 | sim_finish(); 28 | } 29 | #endif 30 | 31 | /** 32 | * Command "mark" 33 | * 34 | * Set a debug marker value 35 | * 36 | */ 37 | #ifdef CSR_SIM_MARKER_BASE 38 | void cmd_sim_mark_handler(int nb_params, char **params) 39 | { 40 | // cannot use param[1] as it is not a const string 41 | sim_mark(NULL); 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/sim.make: -------------------------------------------------------------------------------- 1 | BUILD_DIR?=../build/sim/ 2 | 3 | include $(BUILD_DIR)/software/include/generated/variables.mak 4 | include $(SOC_DIRECTORY)/software/common.mak 5 | 6 | OBJECTS = sim_debug.o sim.o wfg.o donut.o crt0.o main.o 7 | 8 | all: firmware_sim.bin 9 | 10 | %.bin: %.elf 11 | $(OBJCOPY) -O binary $< $@ 12 | chmod -x $@ 13 | 14 | vpath %.a $(PACKAGES:%=../%) 15 | 16 | firmware_sim.elf: $(OBJECTS) 17 | $(CC) $(LDFLAGS) -T linker.ld -N -o $@ \ 18 | $(OBJECTS) \ 19 | $(PACKAGES:%=-L$(BUILD_DIR)/software/%) \ 20 | -Wl,--gc-sections \ 21 | $(LIBS:lib%=-l%) 22 | chmod -x $@ 23 | 24 | # pull in dependency info for *existing* .o files 25 | -include $(OBJECTS:.o=.d) 26 | 27 | donut.o: CFLAGS += -w 28 | 29 | VPATH = $(BIOS_DIRECTORY):$(BIOS_DIRECTORY)/cmds:$(CPU_DIRECTORY) 30 | 31 | %.o: %.cpp 32 | $(compilexx) 33 | 34 | %.o: %.c 35 | $(compile) 36 | 37 | %.o: %.S 38 | $(assemble) 39 | 40 | clean: 41 | $(RM) $(OBJECTS) firmware_sim.elf firmware_sim.bin .*~ *~ *.d 42 | 43 | .PHONY: all clean 44 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/sim_debug.c: -------------------------------------------------------------------------------- 1 | #include "sim_debug.h" 2 | 3 | #include 4 | #include 5 | 6 | // 0 is used as no marker 7 | #define MAX_N_MARKERS (255 - 1) 8 | 9 | #ifdef CSR_SIM_MARKER_BASE 10 | static int n_markers = 0; 11 | static const char *markers[MAX_N_MARKERS] = {0}; 12 | #endif 13 | 14 | void sim_mark(const char *text) { 15 | #ifdef CSR_SIM_MARKER_BASE 16 | if (text == NULL) { 17 | text = "NO COMMENT"; 18 | } 19 | // 0 is not used 20 | int marker_num = n_markers + 1; 21 | markers[n_markers++] = text; 22 | sim_marker_marker_write(marker_num); 23 | if (n_markers >= MAX_N_MARKERS) { 24 | printf("Max number of markers reached\n"); 25 | n_markers = 0; 26 | } 27 | #else 28 | printf("No sim_marker CSR\n"); 29 | #endif 30 | } 31 | 32 | void sim_markers_summary(void) { 33 | #ifdef CSR_SIM_MARKER_BASE 34 | printf("\nMarkers:\n"); 35 | for (int i = 0; i < n_markers; ++i) { 36 | printf(" %3d: %s\n", i + 1, markers[i]); 37 | } 38 | printf("\n"); 39 | #else 40 | printf("No sim_marker CSR\n"); 41 | #endif 42 | } 43 | 44 | void sim_trace(int on) { 45 | #ifdef CSR_SIM_TRACE_BASE 46 | sim_trace_enable_write(on); 47 | #else 48 | printf("No sim_trace CSR\n"); 49 | #endif 50 | } 51 | 52 | int sim_trace_on(void) { 53 | #ifdef CSR_SIM_TRACE_BASE 54 | return sim_trace_enable_read(); 55 | #else 56 | printf("No sim_trace CSR\n"); 57 | return 0; 58 | #endif 59 | } 60 | 61 | void sim_finish(void) { 62 | #ifdef CSR_SIM_FINISH_BASE 63 | sim_trace(0); 64 | if (n_markers > 0) { 65 | sim_markers_summary(); 66 | } 67 | sim_finish_finish_write(1); 68 | #else 69 | printf("No sim_finish CSR\n"); 70 | #endif 71 | } 72 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/sim_debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __SIM_DEBUG_H 2 | #define __SIM_DEBUG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | // add next marker with given comment 9 | void sim_mark(const char *comment); 10 | #define sim_mark_func() sim_mark(__func__) 11 | // print the summary of markers mapping (number -> comment) 12 | void sim_markers_summary(void); 13 | // enable simulation trace dump 14 | void sim_trace(int on); 15 | // check if trace is on 16 | int sim_trace_on(void); 17 | // finish simulation 18 | void sim_finish(void); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /fpga/ulx3s_soc/firmware/wfg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef WFG_BASE 5 | 6 | int cnt = 3; 7 | int cpol = 0; 8 | int lsbfirst = 0; 9 | int dff = 3; 10 | int sspol = 0; 11 | 12 | /* 13 | Write to a register of the waveform generator 14 | 15 | peripheral: 16 | - core - 0x01 17 | - interconnect - 0x02 18 | - stim_sine - 0x03 19 | - stim_mem - 0x04 20 | - drive_spi - 0x05 21 | - drive_pat - 0x06 22 | 23 | address: 24 | The address of the register, can be 0-15 25 | */ 26 | void wfg_set_register(int peripheral, int address, int value) 27 | { 28 | *(volatile int*)(WFG_BASE + (peripheral<<4) + (address & 0xF)) = value; 29 | 30 | int readback = *(volatile int*)(WFG_BASE + (peripheral<<4) + (address & 0xF)); 31 | 32 | if (readback != value) 33 | { 34 | printf("Wrong value: %d != %d\n", readback, value); 35 | } 36 | } 37 | 38 | void wfg_inc_cnt(void) 39 | { 40 | cnt++; 41 | printf("cnt: %d\n", cnt); 42 | wfg_set_register(0x3, 0x8, cnt); // Clock divider 43 | } 44 | 45 | void wfg_dec_cnt(void) 46 | { 47 | cnt--; 48 | printf("cnt: %d\n", cnt); 49 | wfg_set_register(0x3, 0x8, cnt); // Clock divider 50 | } 51 | 52 | void wfg_init(void) 53 | { 54 | //*(volatile int*)(WFG_BASE) = 0xDEADBEEF; 55 | 56 | int core_sync_count = 16; 57 | int core_subcycle_count = 16; 58 | 59 | int subcore_sync_count = 32; 60 | int subcore_subcycle_count = 16; 61 | 62 | // Core 63 | wfg_set_register(0x1, 0x4, (core_sync_count << 0) | (core_subcycle_count << 8)); 64 | wfg_set_register(0x1, 0x0, 1); // Enable 65 | 66 | // Subcore 67 | wfg_set_register(0x2, 0x4, (subcore_sync_count << 0) | (subcore_subcycle_count << 8)); 68 | wfg_set_register(0x2, 0x0, 1); // Enable 69 | 70 | // Interconnect 71 | wfg_set_register(0x3, 0x4, 0); // Driver0 72 | wfg_set_register(0x3, 0x8, 1); // Driver1 73 | wfg_set_register(0x3, 0x0, 1); // Enable 74 | 75 | // Sine 76 | wfg_set_register(0x4, 0x0, 1); // Enable 77 | 78 | // Mem 79 | wfg_set_register(0x5, 0x4, 0x4); // Start 80 | wfg_set_register(0x5, 0x8, 0xF); // End 81 | wfg_set_register(0x5, 0xC, 0x2); // Increment 82 | wfg_set_register(0x5, 0x0, 1); // Enable 83 | 84 | // SPI 85 | wfg_set_register(0x6, 0x8, cnt); // Clock divider 86 | wfg_set_register(0x6, 0x4, (cpol<<0) | (lsbfirst<<1) | (dff<<2) | (sspol<<4)); 87 | wfg_set_register(0x6, 0x0, 1); // Enable SPI 88 | 89 | // Pattern 90 | wfg_set_register(0x7, 0x4, (0) | (8<<8) ); // Start:End 91 | wfg_set_register(0x7, 0x8, 0xFFFFFFFF); // Low bit 92 | wfg_set_register(0x7, 0xC, 0xFFFFFFFF); // High bit 93 | wfg_set_register(0x7, 0x0, 0xFFFFFFFF); // Enable all bits 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /img/WFG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/WFG.png -------------------------------------------------------------------------------- /img/litex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/litex.png -------------------------------------------------------------------------------- /img/utilization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/utilization.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cocotb==1.6.2 2 | cocotb-bus==0.2.1 3 | cocotbext-axi==0.1.18 4 | cocotbext-spi==0.2.0 5 | cocotbext-wishbone==0.2.2 6 | Jinja2==3.1.2 7 | matplotlib==3.5.2 8 | numpy==1.22.3 9 | pytest==7.1.2 10 | scipy==1.8.0 11 | -------------------------------------------------------------------------------- /templating/converter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: © 2022 semify 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | import os 6 | import sys 7 | import csv 8 | import json 9 | import argparse 10 | import pathlib 11 | 12 | def dir_path(string): 13 | path = pathlib.Path(string) 14 | 15 | if not path.exists(): 16 | raise argparse.ArgumentTypeError(f"{path} does not exist") 17 | 18 | if not path.is_dir(): 19 | raise argparse.ArgumentTypeError(f"{path} is not a directory") 20 | 21 | return string 22 | 23 | def file_path(string): 24 | path = pathlib.Path(string) 25 | 26 | if not path.exists(): 27 | raise argparse.ArgumentTypeError(f"{path} does not exist") 28 | 29 | if not path.is_file(): 30 | raise argparse.ArgumentTypeError(f"{path} is not a file") 31 | 32 | return string 33 | 34 | def main(): 35 | parser = argparse.ArgumentParser(description='Generate code blocks from templates inside files.') 36 | parser.add_argument('-i', '--input', type=file_path, nargs='+', required=True, help='input file') 37 | parser.add_argument('-o', '--output', type=str, nargs='+', help='output file, if not set equals input file') 38 | parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') 39 | 40 | args = parser.parse_args() 41 | 42 | # Set file names 43 | in_filenames = [pathlib.Path(input) for input in args.input] 44 | out_filenames = [pathlib.Path(output) for output in args.output] 45 | 46 | if len(in_filenames) != len(out_filenames): 47 | print('Error: The number of input and output files must be equal') 48 | sys.exit(0) 49 | 50 | for (file_cnt, in_filename) in enumerate(in_filenames): 51 | out_filename = out_filenames[file_cnt] 52 | 53 | if in_filename.suffix != ".csv": 54 | print("Currently only .csv files can be converted") 55 | sys.exit(0) 56 | 57 | if args.verbose: 58 | print("in_filename: {}".format(in_filename)) 59 | print("out_filename: {}".format(out_filename)) 60 | 61 | json_dict = {"registers" : {}} 62 | 63 | with open(in_filename, 'r') as f_csv: 64 | csv_reader = csv.DictReader(f_csv) 65 | 66 | last_register = None 67 | 68 | for entry in csv_reader: 69 | if args.verbose: 70 | print("Current entry: {}".format(entry)) 71 | 72 | # Register 73 | if entry['RegName'] != '': 74 | last_register = entry['RegName'] 75 | json_dict["registers"][entry['RegName']] = {"address" : entry['Address'], 76 | "description" : entry['Description'], 77 | "entries" : {} 78 | } 79 | else: 80 | json_dict["registers"][last_register]["entries"][entry['BitName']] = {"access" : entry['Access'], 81 | "hardware" : entry['HW'], 82 | "LSB" : entry['LSB'], 83 | "MSB" : entry['MSB'], 84 | "reset" : entry['Reset'], 85 | "description": entry['Description'] 86 | } 87 | 88 | if args.verbose: 89 | print("Complete dictionary:") 90 | print(json_dict) 91 | 92 | with open(out_filename, "w") as f_json: 93 | json.dump(json_dict, f_json, indent=4, sort_keys=True) 94 | 95 | print("Library conversion done.") 96 | 97 | if __name__ == "__main__": 98 | main() 99 | -------------------------------------------------------------------------------- /templating/templates/wishbone/assign_from_registers.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | 5 | {{'{:<12}'.format(registers[register].address + ':')}} 6 | 7 | {%- if registers[register].entries|length > 1 -%} 8 | begin 9 | {% endif -%} 10 | 11 | {%- for entry in registers[register].entries -%} 12 | {%- set current_entry = registers[register].entries[entry] -%} 13 | 14 | {%- if registers[register].entries|length > 1 -%} 15 | {{'{:<12}wbs_dat_o[{:>2}:{:>2}] <= {};'.format('', current_entry.MSB, current_entry.LSB, register.lower() + '_' + entry.lower() + '_ff')}} 16 | {% else -%} 17 | {{'wbs_dat_o[{:>2}:{:>2}] <= {};'.format(current_entry.MSB, current_entry.LSB, register.lower() + '_' + entry.lower() + '_ff')}} 18 | {% endif -%} 19 | {%- endfor -%} 20 | 21 | {%- if registers[register].entries|length > 1 -%} 22 | end 23 | {% endif -%} 24 | 25 | {%- endfor %} 26 | -------------------------------------------------------------------------------- /templating/templates/wishbone/assign_outputs.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set current_entry = registers[register].entries[entry] -%} 6 | {{ 'assign {:<24} = {};'.format(register.lower()+'_'+entry.lower()+'_q_o', register.lower() + '_' + entry.lower() + '_ff') }} 7 | {% endfor -%} 8 | {%- endfor %} 9 | -------------------------------------------------------------------------------- /templating/templates/wishbone/assign_to_module.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set inner_loop = loop -%} 6 | {%- set current_entry = registers[register].entries[entry] -%} 7 | 8 | {%- if current_entry.hardware == 'cfg' -%} 9 | 10 | {#- Check if there is another cfg register after me -#} 11 | 12 | {%- set ns = namespace(iamlast=true, foundme=false) -%} 13 | 14 | {% for other_register in registers -%} 15 | {%- set other_outer_loop = loop -%} 16 | {%- for other_entry in registers[other_register].entries -%} 17 | {%- set other_inner_loop = loop -%} 18 | 19 | {%- if ns.foundme == true -%} 20 | {%- if registers[other_register].entries[other_entry].hardware == 'cfg' -%} 21 | {%- set ns.iamlast = false -%} 22 | {%- endif -%} 23 | {%- endif -%} 24 | 25 | {%- if register == other_register and other_entry == entry -%} 26 | {%- set ns.foundme = true -%} 27 | {%- endif -%} 28 | {%- endfor -%} 29 | {%- endfor -%} 30 | 31 | {%- set delimiter = ',' -%} 32 | {%- if ns.iamlast == 1 -%} 33 | {%- set delimiter = '' -%} 34 | {%- endif -%} 35 | 36 | {{ '.{:<24}({:<24}){:<2}// {}'.format(register.lower()+'_'+entry.lower()+'_q_o', register.lower()+'_'+entry.lower()+'_q', delimiter, register + '.' + entry + ' register output') }} 37 | {% endif -%} 38 | {%- endfor -%} 39 | {%- endfor %} 40 | -------------------------------------------------------------------------------- /templating/templates/wishbone/assign_to_registers.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | 5 | {{'{:<12}'.format(registers[register].address + ':')}} 6 | 7 | {%- if registers[register].entries|length > 1 -%} 8 | begin 9 | {% endif -%} 10 | 11 | {%- for entry in registers[register].entries -%} 12 | {%- set current_entry = registers[register].entries[entry] -%} 13 | 14 | {%- if registers[register].entries|length > 1 -%} 15 | {{'{:<12}{:<24} <= wbs_dat_i[{:>2}:{:>2}];'.format('', register.lower() + '_' + entry.lower() + '_ff', current_entry.MSB, current_entry.LSB)}} 16 | {% else -%} 17 | {{'{:<24} <= wbs_dat_i[{:>2}:{:>2}];'.format(register.lower() + '_' + entry.lower() + '_ff', current_entry.MSB, current_entry.LSB)}} 18 | {% endif -%} 19 | {%- endfor -%} 20 | 21 | 22 | {%- if registers[register].entries|length > 1 -%} 23 | end 24 | {% endif -%} 25 | 26 | {%- endfor %} 27 | -------------------------------------------------------------------------------- /templating/templates/wishbone/instantiate_registers.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set current_entry = registers[register].entries[entry] -%} 6 | {%- if current_entry.MSB == current_entry.LSB -%} 7 | {%- set name = register.lower()+"_"+entry.lower()+"_ff;" -%} 8 | {{ 'logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_ff;', register + '.' + entry + ' FF') }} 9 | {% else -%} 10 | {%- set name = "["+current_entry.MSB+":"+current_entry.LSB+"] "+register.lower()+"_"+entry.lower()+"_ff;" -%} 11 | {{ 'logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_ff;', register + '.' + entry + ' FF') }} 12 | {% endif -%} 13 | {%- endfor -%} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /templating/templates/wishbone/instantiate_top.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set current_entry = registers[register].entries[entry] -%} 6 | {%- if current_entry.MSB == current_entry.LSB -%} 7 | {%- set name = register.lower()+"_"+entry.lower()+"_ff;" -%} 8 | {{ 'logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_q;', register + '.' + entry + ' register output') }} 9 | {% else -%} 10 | {%- set name = "["+current_entry.MSB+":"+current_entry.LSB+"] "+register.lower()+"_"+entry.lower()+"_ff;" -%} 11 | {{ 'logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_q;', register + '.' + entry + ' register output') }} 12 | {% endif -%} 13 | {%- endfor -%} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /templating/templates/wishbone/register_interface.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set current_entry = registers[register].entries[entry] -%} 6 | 7 | {%- set delimiter = ',' -%} 8 | {%- if outer_loop.last and loop.last -%} 9 | {%- set delimiter = '' -%} 10 | {%- endif -%} 11 | 12 | {%- if current_entry.MSB == current_entry.LSB -%} 13 | {{ 'output logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_q_o'+delimiter, register + '.' + entry + ' register output') }} 14 | {% else -%} 15 | {{ 'output logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_q_o'+delimiter, register + '.' + entry + ' register output') }} 16 | {% endif -%} 17 | {%- endfor -%} 18 | {%- endfor %} 19 | -------------------------------------------------------------------------------- /templating/templates/wishbone/register_overview.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set address_int = register.address | int(base=16) -%} 4 | {%- set bits_int = register.bits | int -%} 5 | // 0x{{ '%0x' % address_int }} : Data signal of {{ register.name }} 6 | // bit {{ bits_int - 1 }}~0 - {{ register.name }}[{{ bits_int - 1 }}:0] (Read/Write) 7 | // others - reserved 8 | {% endfor -%} 9 | -------------------------------------------------------------------------------- /templating/templates/wishbone/reset_registers.template: -------------------------------------------------------------------------------- 1 | 2 | {% for register in registers -%} 3 | {%- set outer_loop = loop -%} 4 | {%- for entry in registers[register].entries -%} 5 | {%- set current_entry = registers[register].entries[entry] -%} 6 | {{ '%-24s <= %s;' | format(register.lower() + '_' + entry.lower() + '_ff', current_entry.reset) }} 7 | {% endfor -%} 8 | {%- endfor %} 9 | --------------------------------------------------------------------------------