From a9bef5f79309ad94b68874e69be982aaf5796808 Mon Sep 17 00:00:00 2001 From: mole99 Date: Fri, 29 Mar 2024 19:07:14 +0100 Subject: [PATCH] Integrate SPI controller --- .github/workflows/test.yaml | 2 +- .gitignore | 2 + src/shader_memory.sv | 16 ++- src/spi_receiver.sv | 194 +++++++++++++----------------- src/synchronizer.sv | 1 - src/timing.sv | 1 - src/tiny_shader_top.sv | 67 +++++++++-- src/tt_um_tiny_shader_mole99.sv | 34 ++++-- test/tb.v | 28 +++++ test/test.py | 205 +++++++++++++++++++++++++++++++- 10 files changed, 409 insertions(+), 141 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8ebf5aa..20e5508 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -49,4 +49,4 @@ jobs: path: | test/tb.fst test/results.xml - test/frame.png + test/default.png diff --git a/.gitignore b/.gitignore index cf3385a..d8bf585 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .vscode *.vcd *.fst +*.fst.hier runs tt_submission src/user_config.tcl @@ -10,3 +11,4 @@ test/sim_build test/__pycache__/ test/results.xml test/gate_level_netlist.v +frame.png diff --git a/src/shader_memory.sv b/src/shader_memory.sv index 26c8ad8..4146248 100644 --- a/src/shader_memory.sv +++ b/src/shader_memory.sv @@ -9,17 +9,19 @@ module shader_memory #( input logic clk_i, input logic rst_ni, input logic shift_i, + input logic load_i, + input logic [7:0] instr_i, output logic [7:0] instr_o ); logic [7:0] memory [NUM_INSTR]; int i; - // Initialize the memory and shift the memory - // by a whole word if shift_i is high + // Initialize the memory on reset + // Shift the memory by a whole word if shift_i is high always_ff @(posedge clk_i, negedge rst_ni) begin if (!rst_ni) begin `ifdef COCOTB_SIM - $readmemb("../sw/binary/test2.bit", memory); + $readmemb("../sw/binary/test4.bit", memory); `else // Load the default program memory[0] <= 8'b00_0100_00; // GETX R0 @@ -37,7 +39,13 @@ module shader_memory #( if (i < NUM_INSTR-1) begin memory[i] <= memory[i+1]; end else begin - memory[i] <= memory[0]; + // Load a new word from externally + if (load_i) begin + memory[i] <= instr_i; + // Else just shift circularily + end else begin + memory[i] <= memory[0]; + end end end end diff --git a/src/spi_receiver.sv b/src/spi_receiver.sv index b1b9ddd..f4e7641 100644 --- a/src/spi_receiver.sv +++ b/src/spi_receiver.sv @@ -4,45 +4,44 @@ `default_nettype none module spi_receiver #( - parameter COLOR1_DEFAULT=0, // color1 default value - parameter COLOR2_DEFAULT=0, // color2 default value - parameter COLOR3_DEFAULT=0, // color3 default value - parameter COLOR4_DEFAULT=0, // color4 default value - parameter MISC_DEFAULT=0 // misc default value + parameter NUM_REGS = 4, + parameter REG_SIZE = 8, + parameter [NUM_REGS*REG_SIZE-1:0] REG_DEFAULTS = '0 )( - input logic clk, // clock - input logic reset_n, // reset active low + input logic clk_i, // clock + input logic rst_ni, // reset active low // SPI signals - input logic spi_sclk, - input logic spi_mosi, - output logic spi_miso, - input logic spi_cs, - - input logic sprite_data, // current sprite data - output logic spi_sprite_shift, // shift pulse - output logic spi_sprite_mode, // shift new data into sprite - output logic spi_mosi_sync, // spi input data - - output logic shift_x, // shift new x position - output logic shift_y, // shift new y position - - // Output register - output logic [5:0] color1, // color1 register - output logic [5:0] color2, // color2 register - output logic [5:0] color3, // color3 register - output logic [5:0] color4, // color4 register - output logic [4:0] misc // miscellaneous register + input logic spi_sclk_i, + input logic spi_mosi_i, + output logic spi_miso_o, + input logic spi_cs_i, + + // Mode signal + // '0' = command mode + // '1' = data mode + input logic mode_i, + + input logic [7:0] memory_instr_i, // current sprite data + output logic [7:0] memory_instr_o, // current sprite data + output logic memory_shift_o, // shift pulse + output logic memory_load_o, // shift new data into sprite + + // Output registers + output logic [NUM_REGS*REG_SIZE-1:0] registers_o ); + logic [NUM_REGS*REG_SIZE-1:0] registers; + // Synchronizer to prevent metastability + logic spi_mosi_sync; synchronizer #( .FF_COUNT(2) ) synchronizer_spi_mosi ( - .clk (clk), - .reset_n (reset_n), - .in (spi_mosi), + .clk (clk_i), + .reset_n (rst_ni), + .in (spi_mosi_i), .out (spi_mosi_sync) ); @@ -50,9 +49,9 @@ module spi_receiver #( synchronizer #( .FF_COUNT(2) ) synchronizer_spi_cs ( - .clk (clk), - .reset_n (reset_n), - .in (spi_cs), + .clk (clk_i), + .reset_n (rst_ni), + .in (spi_cs_i), .out (spi_cs_sync) ); @@ -60,17 +59,16 @@ module spi_receiver #( synchronizer #( .FF_COUNT(2) ) synchronizer_spi_sclk ( - .clk (clk), - .reset_n (reset_n), - .in (spi_sclk), + .clk (clk_i), + .reset_n (rst_ni), + .in (spi_sclk_i), .out (spi_sclk_sync) ); - // Detect spi_clk edge logic spi_sclk_delayed; - always_ff @(posedge clk) begin + always_ff @(posedge clk_i) begin spi_sclk_delayed <= spi_sclk_sync; end @@ -80,79 +78,57 @@ module spi_receiver #( // State Machine - logic [2:0] spi_cmd; + logic [7:0] spi_cmd; logic [2:0] spi_cnt; logic spi_mode; - typedef enum bit [2:0] { - CMD_SPRITE_DATA = 3'd0, - CMD_COLOR1 = 3'd1, - CMD_COLOR2 = 3'd2, - CMD_COLOR3 = 3'd3, - CMD_COLOR4 = 3'd4, - CMD_SPRITE_X = 3'd5, - CMD_SPRITE_Y = 3'd6, - CMD_MISC = 3'd7 - } spi_cmds_t; - - logic spi_cmd_sprite_data; - logic spi_cmd_color1; - logic spi_cmd_color2; - logic spi_cmd_color3; - logic spi_cmd_color4; - logic spi_cmd_sprite_x; - logic spi_cmd_sprite_y; - logic spi_cmd_misc; - - assign spi_cmd_sprite_data = spi_cmd == CMD_SPRITE_DATA; - assign spi_cmd_color1 = spi_cmd == CMD_COLOR1; - assign spi_cmd_color2 = spi_cmd == CMD_COLOR2; - assign spi_cmd_color3 = spi_cmd == CMD_COLOR3; - assign spi_cmd_color4 = spi_cmd == CMD_COLOR4; - assign spi_cmd_sprite_x = spi_cmd == CMD_SPRITE_X; - assign spi_cmd_sprite_y = spi_cmd == CMD_SPRITE_Y; - assign spi_cmd_misc = spi_cmd == CMD_MISC; - - logic first_data_sprite; - - always_ff @(posedge clk, negedge reset_n) begin - if (!reset_n) begin + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin spi_mode <= 1'b0; - spi_cnt <= '0; - spi_miso <= 1'b0; + spi_cnt <= 3'd0; + spi_cmd <= 8'b0; + + registers <= REG_DEFAULTS; - color1 <= COLOR1_DEFAULT; - color2 <= COLOR2_DEFAULT; - color3 <= COLOR3_DEFAULT; - color4 <= COLOR4_DEFAULT; + spi_miso_o <= 1'b0; - misc <= MISC_DEFAULT; - - first_data_sprite <= 1'b0; + memory_shift_o <= 1'b0; + memory_load_o <= 1'b0; end else begin + memory_shift_o <= 1'b0; + memory_load_o <= 1'b0; + if (!spi_cs_sync && spi_sclk_falling) begin // Read the command if (spi_mode == 1'b0) begin - spi_cmd <= {spi_cmd[1:0], spi_mosi_sync}; + spi_cmd <= {spi_cmd[6:0], spi_mosi_sync}; spi_cnt <= spi_cnt + 1; if (spi_cnt == 7) begin - spi_mode <= 1'b1; + if (mode_i == 0) begin + // Read the command + spi_mode <= 1'b1; + end else begin + memory_shift_o <= 1'b1; + memory_load_o <= 1'b1; + end end // Write the data depending on the command end else begin - case (1'b1) - spi_cmd_color1: color1 <= {color1[4:0], spi_mosi_sync}; - spi_cmd_color2: color2 <= {color2[4:0], spi_mosi_sync}; - spi_cmd_color3: color3 <= {color3[4:0], spi_mosi_sync}; - spi_cmd_color4: color4 <= {color4[4:0], spi_mosi_sync}; - spi_cmd_sprite_data: first_data_sprite <= 1'b1; - spi_cmd_misc: misc <= {misc[3:0], spi_mosi_sync}; + case (spi_cmd) + 3'd0: registers[0*8 +: 8] <= {registers[0*8 +: 7], spi_mosi_sync}; + 3'd1: registers[1*8 +: 8] <= {registers[1*8 +: 7], spi_mosi_sync}; + 3'd2: registers[2*8 +: 8] <= {registers[2*8 +: 7], spi_mosi_sync}; + 3'd3: registers[3*8 +: 8] <= {registers[3*8 +: 7], spi_mosi_sync}; + 3'd4: registers[4*8 +: 8] <= {registers[4*8 +: 7], spi_mosi_sync}; + 3'd5: registers[5*8 +: 8] <= {registers[5*8 +: 7], spi_mosi_sync}; + 3'd6: registers[6*8 +: 8] <= {registers[6*8 +: 7], spi_mosi_sync}; + 3'd7: registers[7*8 +: 8] <= {registers[7*8 +: 7], spi_mosi_sync}; endcase spi_cnt <= spi_cnt + 1; - if (spi_cnt == 7 && !spi_sprite_mode) begin + if (spi_cnt == 7) begin spi_mode <= 1'b0; end end @@ -161,33 +137,23 @@ module spi_receiver #( // Echo back the previous values if (!spi_cs_sync && spi_sclk_rising) begin if (spi_mode == 1'b1) begin - case (1'b1) - spi_cmd_color1: spi_miso <= color1[5]; - spi_cmd_color2: spi_miso <= color2[5]; - spi_cmd_color3: spi_miso <= color3[5]; - spi_cmd_color4: spi_miso <= color4[5]; - spi_cmd_sprite_data: spi_miso <= sprite_data; - spi_cmd_misc: spi_miso <= misc[4]; + case (spi_cmd) + 3'd0: spi_miso_o <= registers[0*8 + 7]; + 3'd1: spi_miso_o <= registers[1*8 + 7]; + 3'd2: spi_miso_o <= registers[2*8 + 7]; + 3'd3: spi_miso_o <= registers[3*8 + 7]; + 3'd4: spi_miso_o <= registers[4*8 + 7]; + 3'd5: spi_miso_o <= registers[5*8 + 7]; + 3'd6: spi_miso_o <= registers[6*8 + 7]; + 3'd7: spi_miso_o <= registers[7*8 + 7]; endcase end end - - // Loading sprite data can be interrupted by rising cs - // after one bit was shifted into the sprite. - // This means it is possible to e.g. shift 100 bits and not only 8 at once - if (first_data_sprite && spi_sprite_mode && spi_cs_sync) begin - spi_mode <= 1'b0; - first_data_sprite <= 1'b0; - end end end - - // Enable sprite data shifting - assign spi_sprite_mode = spi_mode && spi_cmd_sprite_data; - assign spi_sprite_shift = spi_sprite_mode && spi_sclk_falling; - - // Indicate we want to shift a new position - assign shift_x = spi_mode && spi_cmd_sprite_x && spi_sclk_falling; - assign shift_y = spi_mode && spi_cmd_sprite_y && spi_sclk_falling; - + + assign registers_o = registers; + + assign memory_instr_o = spi_cmd; + endmodule diff --git a/src/synchronizer.sv b/src/synchronizer.sv index 093cb30..4621288 100644 --- a/src/synchronizer.sv +++ b/src/synchronizer.sv @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: © 2022 Leo Moser // SPDX-License-Identifier: Apache-2.0 -`timescale 1ns / 1ps `default_nettype none module synchronizer #( diff --git a/src/timing.sv b/src/timing.sv index 2153028..6f047c3 100644 --- a/src/timing.sv +++ b/src/timing.sv @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: © 2022 Leo Moser // SPDX-License-Identifier: Apache-2.0 -`timescale 1ns/1ps `default_nettype none module timing #( diff --git a/src/tiny_shader_top.sv b/src/tiny_shader_top.sv index 895e6e9..4117748 100644 --- a/src/tiny_shader_top.sv +++ b/src/tiny_shader_top.sv @@ -8,14 +8,22 @@ module tiny_shader_top ( input logic rst_ni, // SPI signals - // TODO + input logic spi_sclk_i, + input logic spi_mosi_i, + output logic spi_miso_o, + input logic spi_cs_i, + + // Mode signal + // '0' = command mode + // '1' = data mode + input logic mode_i, // SVGA signals - output logic [5:0] rrggbb_o, - output logic hsync_o, - output logic vsync_o, - output logic next_vertical_o, - output logic next_frame_o + output logic [5:0] rrggbb_o, + output logic hsync_o, + output logic vsync_o, + output logic next_vertical_o, + output logic next_frame_o ); /* @@ -144,7 +152,50 @@ module tiny_shader_top ( cs_active_low = True */ + logic [7:0] memory_instr; + logic memory_shift; + logic memory_load; + + localparam NUM_REGS = 4; + localparam REG_SIZE = 8; + // TODO + logic [NUM_REGS*REG_SIZE-1:0] registers; + + logic [7:0] reg0, reg1, reg2, reg3; + + assign reg0 = registers[0*8 +: 8]; + assign reg1 = registers[1*8 +: 8]; + assign reg2 = registers[2*8 +: 8]; + assign reg3 = registers[3*8 +: 8]; + + spi_receiver #( + .NUM_REGS (NUM_REGS), + .REG_SIZE (REG_SIZE), + .REG_DEFAULTS ({NUM_REGS{8'b0}}) + ) spi_receiver_inst ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // SPI signals + .spi_sclk_i (spi_sclk_i), + .spi_mosi_i (spi_mosi_i), + .spi_miso_o (spi_miso_o), + .spi_cs_i (spi_cs_i), + + // Mode signal + .mode_i (mode_i), + + .memory_instr_i (instr), + .memory_instr_o (memory_instr), + .memory_shift_o (memory_shift), + .memory_load_o (memory_load), + + // Output registers + .registers_o (registers) + ); + + // Graphics logic [5:0] counter_h_small; logic [5:0] counter_v_small; @@ -161,7 +212,9 @@ module tiny_shader_top ( shader_memory shader_memory_inst ( .clk_i (clk_i), .rst_ni (rst_ni), - .shift_i (execute_shader), + .shift_i (execute_shader || memory_shift), + .load_i (memory_load), + .instr_i (memory_instr), .instr_o (instr) ); diff --git a/src/tt_um_tiny_shader_mole99.sv b/src/tt_um_tiny_shader_mole99.sv index 123f536..a6d5821 100644 --- a/src/tt_um_tiny_shader_mole99.sv +++ b/src/tt_um_tiny_shader_mole99.sv @@ -26,6 +26,13 @@ module tt_um_tiny_shader_mole99 ( logic next_vertical; logic next_frame; + logic spi_sclk; + logic spi_mosi; + logic spi_miso; + logic spi_cs; + + logic mode; + tiny_shader_top #( ) tiny_shader_top_inst ( @@ -33,7 +40,13 @@ module tt_um_tiny_shader_mole99 ( .rst_ni (rst_n_sync), // SPI signals - // TODO + .spi_sclk_i (spi_sclk), + .spi_mosi_i (spi_mosi), + .spi_miso_o (spi_miso), + .spi_cs_i (spi_cs), + + // Mode signal + .mode_i (mode), // SVGA signals .rrggbb_o (rrggbb), @@ -64,11 +77,11 @@ module tt_um_tiny_shader_mole99 ( // Bidir PMOD - SPI and additional signals - // Top row - assign uio_out[0] = 1'b0; assign uio_oe[0] = 1'b0; - assign uio_out[1] = 1'b0; assign uio_oe[1] = 1'b0; - assign uio_out[2] = 1'b0; assign uio_oe[2] = 1'b0; - assign uio_out[3] = 1'b0; assign uio_oe[3] = 1'b0; + // Top row - SPI + assign spi_cs = uio_in[0]; assign uio_oe[0] = 1'b0; assign uio_out[0] = 1'b0; + assign spi_mosi = uio_in[1]; assign uio_oe[1] = 1'b0; assign uio_out[1] = 1'b0; + assign uio_out[2] = spi_miso; assign uio_oe[2] = 1'b1; + assign spi_sclk = uio_in[3]; assign uio_oe[3] = 1'b0; assign uio_out[3] = 1'b0; // Bottom row assign uio_out[4] = 1'b0; assign uio_oe[4] = 1'b0; @@ -76,11 +89,10 @@ module tt_um_tiny_shader_mole99 ( assign uio_out[6] = 1'b0; assign uio_oe[6] = 1'b0; assign uio_out[7] = 1'b0; assign uio_oe[7] = 1'b0; - // Input PMOD - not used - - /* - ui_in[0] - ui_in[1] + // Input PMOD - mode + + assign mode = ui_in[0]; + /*ui_in[1] ui_in[2] ui_in[3] ui_in[4] diff --git a/test/tb.v b/test/tb.v index 6931660..1a419fb 100644 --- a/test/tb.v +++ b/test/tb.v @@ -11,10 +11,20 @@ module tb (); $dumpfile("tb.fst"); $dumpvars(0, tb); `ifndef GL_TEST + // Dump execute regs $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_execute_inst.regs[0]); $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_execute_inst.regs[1]); $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_execute_inst.regs[2]); $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_execute_inst.regs[3]); + // Dump shader memory + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[0]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[1]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[2]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[3]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[4]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[5]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[6]); + $dumpvars(0, tb.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[7]); `endif #1; end @@ -34,6 +44,13 @@ module tb (); wire [5:0] rrggbb; wire hsync; wire vsync; + + wire spi_sclk; + wire spi_mosi; + wire spi_miso; + wire spi_cs; + + wire mode; // Output PMOD - Tiny VGA @@ -43,7 +60,18 @@ module tb (); assign vsync = uo_out[3]; assign hsync = uo_out[7]; + + // Bidir PMOD - SPI and additional signals + + // Top row - SPI + assign uio_in[0] = spi_cs; + assign uio_in[1] = spi_mosi; + assign spi_miso = uio_out[2]; + assign uio_in[3] = spi_sclk; + // Input PMOD - mode + + assign ui_in[0] = mode; // Replace tt_um_example with your module name: tt_um_tiny_shader_mole99 tt_um_tiny_shader_mole99_inst ( diff --git a/test/test.py b/test/test.py index e121c88..422203b 100644 --- a/test/test.py +++ b/test/test.py @@ -2,12 +2,15 @@ # SPDX-License-Identifier: Apache-2.0 from PIL import Image, ImageChops +import random import cocotb from cocotb.clock import Clock from cocotb.triggers import ClockCycles from cocotb.triggers import Timer, RisingEdge, FallingEdge +from cocotbext.spi import SpiBus, SpiConfig, SpiMaster + # VGA Parameters WIDTH = 640; HEIGHT = 480; @@ -76,8 +79,14 @@ async def draw_frame(dut): else: screen_x += 1 +# Send cmd and payload over SPI +async def spi_send_cmd(dut, spi_master, cmd, data, burst=False): + print(f'CMD: {cmd} DATA: {data}') + await spi_master.write(cmd) + await spi_master.write(data, burst=burst) + @cocotb.test() -async def test_vga(dut): +async def test_vga_default(dut): dut._log.info("Starting test_vga") # Start the clock @@ -85,6 +94,8 @@ async def test_vga(dut): await cocotb.start(c.start()) # Assign default values + dut.ena.value = 1 + dut.mode.value = 0 # cmd mode # Reset await reset_dut(dut.rst_n, 50) @@ -100,6 +111,196 @@ async def test_vga(dut): dut._log.info("Test") image = await taks_draw_frame.join() - image.save(f"frame.png") + image.save(f"default.png") + + await ClockCycles(dut.clk, 10) + +@cocotb.test() +async def test_spi_regs(dut): + + # Start the clock + c = Clock(dut.clk, 10, 'ns') + await cocotb.start(c.start()) + + spi_bus = SpiBus.from_prefix(dut, "spi") + + spi_config = SpiConfig( + word_width = 8, + sclk_freq = 2e6, + cpol = False, + cpha = True, + msb_first = True, + frame_spacing_ns = 500 + ) + + spi_master = SpiMaster(spi_bus, spi_config) + + # Assign default values + dut.ena.value = 1 + dut.mode.value = 0 # cmd mode + + # Reset + await reset_dut(dut.rst_n, 50) + dut._log.info("Reset done") + + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg1.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg2.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg3.value == 0x00) + + # Set reg0 + await spi_send_cmd(dut, spi_master, [0x0], [0x03]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == 0x03) + + # Set reg1 + await spi_send_cmd(dut, spi_master, [0x1], [0x70]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg1.value == 0x70) + + # Set reg2 + await spi_send_cmd(dut, spi_master, [0x2], [0xEA]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg2.value == 0xEA) + + # Set reg3 + await spi_send_cmd(dut, spi_master, [0x3], [0xF3]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg3.value == 0xF3) + + # Set reg0 + await spi_send_cmd(dut, spi_master, [0x0], [0x42]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == 0x42) + + await ClockCycles(dut.clk, 10) + +@cocotb.test() +async def test_spi_shader(dut): + + # Start the clock + c = Clock(dut.clk, 10, 'ns') + await cocotb.start(c.start()) + + spi_bus = SpiBus.from_prefix(dut, "spi") + + spi_config = SpiConfig( + word_width = 8, + sclk_freq = 2e6, + cpol = False, + cpha = True, + msb_first = True, + frame_spacing_ns = 500 + ) + + spi_master = SpiMaster(spi_bus, spi_config) + + # Assign default values + dut.ena.value = 1 + dut.mode.value = 1 # data mode + + # Reset + await reset_dut(dut.rst_n, 50) + dut._log.info("Reset done") + + # Send shader instructions + shader = [0x12, 0x00, 0xFF, 0x64, 0x42, 0x11, 0x97, 0x0F] + await spi_master.write(shader, burst=True) + + # Verify shader was correctly written + for i in range(8): + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[i].value == shader[i]) + + await ClockCycles(dut.clk, 10) + +@cocotb.test() +async def test_spi_regs_shader_regs_random(dut): + + # Start the clock + c = Clock(dut.clk, 10, 'ns') + await cocotb.start(c.start()) + + spi_bus = SpiBus.from_prefix(dut, "spi") + + spi_config = SpiConfig( + word_width = 8, + sclk_freq = 2e6, + cpol = False, + cpha = True, + msb_first = True, + frame_spacing_ns = 500 + ) + + spi_master = SpiMaster(spi_bus, spi_config) + + # Assign default values + dut.ena.value = 1 + dut.mode.value = 0 # cmd mode + + # Reset + await reset_dut(dut.rst_n, 50) + dut._log.info("Reset done") + + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg1.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg2.value == 0x00) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg3.value == 0x00) + + # Set reg0 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x0], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == data) + + # Set reg1 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x1], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg1.value == data) + + # Set reg2 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x2], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg2.value == data) + + # Set reg3 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x3], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg3.value == data) + + # Set reg0 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x0], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == data) + + dut.mode.value = 1 # data mode + + # Send shader instructions + shader = [random.randint(0, 255) for i in range(8)] + await spi_master.write(shader, burst=True) + + # Verify shader was correctly written + for i in range(8): + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.shader_memory_inst.memory[i].value == shader[i]) await ClockCycles(dut.clk, 10) + + dut.mode.value = 0 # cmd mode + + # Set reg0 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x0], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == data) + + # Set reg1 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x1], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg1.value == data) + + # Set reg2 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x2], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg2.value == data) + + # Set reg3 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x3], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg3.value == data) + + # Set reg0 + data = random.randint(0, 255) + await spi_send_cmd(dut, spi_master, [0x0], [data]) + assert(dut.tt_um_tiny_shader_mole99_inst.tiny_shader_top_inst.reg0.value == data)