From b44a3a201ee2b524f31aa93abfe4abd8b756a533 Mon Sep 17 00:00:00 2001 From: Wojciech Kosior Date: Sat, 21 Nov 2020 21:35:54 +0100 Subject: add miscellaneous module, which controls led2 and button2 and provides a timer; include a testbench for timer and led --- design/miscellaneous_slave.v | 133 ++++++++++++++++++++++++++++++ design/soc.v | 31 ++++--- tests/soc_measure_time/Makefile | 8 ++ tests/soc_measure_time/instructions.s.tcl | 57 +++++++++++++ tests/soc_measure_time/test.v | 82 ++++++++++++++++++ 5 files changed, 300 insertions(+), 11 deletions(-) create mode 100644 design/miscellaneous_slave.v create mode 100644 tests/soc_measure_time/Makefile create mode 100644 tests/soc_measure_time/instructions.s.tcl create mode 100644 tests/soc_measure_time/test.v diff --git a/design/miscellaneous_slave.v b/design/miscellaneous_slave.v new file mode 100644 index 0000000..7274db7 --- /dev/null +++ b/design/miscellaneous_slave.v @@ -0,0 +1,133 @@ +/* + * This wb slave controls buttons, LEDs (only one LED, actually) and timer. + * + * Registers and their addresses: + * button2 clicks - address 1 + * led2 state - address 3 + * timer (low bits) - address 4 + * timer (high bits) - address 5 + * Addresses 0, 2, 6 and 7 are not used (undefined contents). + * + * The button register resets to 0. Its content increases by 1 + * every time button is clicked. Button register can also be set to an arbitrary + * value by writing to it. + * + * Writing any non-0 value to led register causes led to be turned on. + * Writing 0 to led register causes led to be turned off. Reading + * led register gives hFFFF if led is on or h0000 otherwire. Led is + * switched off on reset. + * + * Timer has 32 bits and increases by one every clock tick. It starts + * with value 0 on reset. Low 16 bits of timer and high 16 bits of timer are + * available at addresses 4 and 5, respectively. Those addresses can also be + * written to, if one desires to set the clock to a certain value. Care must + * be taken when reading the timer, because between the read of first + * and second half, the bottom part might overlap to 0, causing the high part + * to increase by 1. Because of that, the correct procedure is to read one of + * timer registers twice and compare the values. Of course, when measuring + * very short or very long times, it might be justifiable to only read one half + * of the timer. + */ +`default_nettype none + +module miscellaneous_slave + #( + /* + * Assuming CLK_I is 12.5 MHz, we'd need around 22 and half a bit + * to achieve half-second cooldown. Passig a lower value here makes + * it easier to create a simulation that uses buttons and also manages + * to finish running b4 trumpets for the last judgement sound... + */ + parameter BUTTON_COOLDOWN_BITS = 17 + ) + ( + output wire ACK_O, + input wire CLK_I, + input wire [2:0] ADR_I, + input wire [15:0] DAT_I, + output wire [15:0] DAT_O, + input wire RST_I, + input wire STB_I, + input wire WE_I, + output wire STALL_O, + + /* Non-wishbone */ + /* no button1, since it is reserved to be used as reset button */ + input wire button2, + /* no led2, since it is reserved for signalling when cpu stops operating */ + output wire led2 + ); + + reg [15:0] button2_clicks; + reg [1:0] button2_latched; + /* + * After button is released we measure a cooldown time b4 recording another + * click. Without this, a single button click could be recorded as many + * clicks (I know from my own experience with the board). + */ + reg [BUTTON_COOLDOWN_BITS-1:0] button2_cooldown; + + reg led2_state; + assign led2 = ~led2_state; + + reg [31:0] timer_count; + + reg [15:0] output_data; + reg ack; + + assign DAT_O = output_data; + assign ACK_O = ack; + assign STALL_O = 0; + + always @ (posedge CLK_I) begin + /* Latch and also invert button signals (the inputs are active low) */ + button2_latched <= {button2_latched[0], ~button2}; + + if (!button2_cooldown && button2_latched == 2'b01) + button2_clicks <= button2_clicks + 1; /* might get overwritten below */ + + if (button2_latched[0]) + button2_cooldown <= -1; + else if (button2_cooldown) + button2_cooldown <= button2_cooldown - 1; /* might get overwritten below */ + + output_data <= 16'hXXXX; /* might get overwritten below */ + + if (RST_I) begin + ack <= 0; + button2_clicks <= 0; + button2_cooldown <= -1; + led2_state <= 0; + timer_count <= 0; + end else begin + ack <= STB_I; + + timer_count <= timer_count + 1; + + if (STB_I) begin + case (ADR_I) + 1 : begin + output_data <= button2_clicks; + if (WE_I) + button2_clicks <= DAT_I; + end + 3 : begin + output_data <= {16{led2_state}}; + if (WE_I) + led2_state <= DAT_I != 0; + end + 4 : begin + output_data <= timer_count[15:0]; + if (WE_I) + timer_count[15:0] <= DAT_I; + end + 5 : begin + output_data <= timer_count[31:16]; + if (WE_I) + timer_count[31:16] <= DAT_I; + end + endcase // case (ADR_I) + end // if (STB_I) + end // else: !if(RST_I) + end // always @ (posedge CLK_I) +endmodule // miscellaneous diff --git a/design/soc.v b/design/soc.v index b395404..4171f1d 100644 --- a/design/soc.v +++ b/design/soc.v @@ -6,7 +6,7 @@ * slave 2 - VGA text-mode controller * slave 3 - SPI master controller * slave 4 - UART controller (yet to be added) - * slave 5 - miscellaneous registers (yet to be added) + * slave 5 - miscellaneous registers * * The memory map from stack machine's viewpoint is as follows: * h000000 - h0001FF - embedded RAM @@ -24,7 +24,7 @@ * h140208 - h1403FF - undefined (actually, repetitions of SPI regs) * h140400 - h17FFFF - undefined (actually, repetitions of SPI memory & regs) * h180000 - h1BFFFF - UART (not implemented yet) - * h1C0000 - h1FFFFF - miscellaneous peripherals (not implemented yet) + * h1C0000 - h1FFFFF - miscellaneous peripherals */ `default_nettype none @@ -213,18 +213,28 @@ module soc .ss_n(spi_ss_n) ); - /* - * Slaves 4 and 5 will be UART controller and miscellaneous registers, - * but for now - they're omitted - */ + miscellaneous_slave slave5 + ( + .ACK_O(S5_ACK_O), + .CLK_I(CLK), + .ADR_I(S5_ADR_I[2:0]), + .DAT_I(S5_DAT_I), + .DAT_O(S5_DAT_O), + .RST_I(RST), + .STB_I(S5_STB_I), + .WE_I(S5_WE_I), + .STALL_O(S5_STALL_O), + + /* Non-wishbone */ + .button2(button2), + .led2(led2) + ); + + /* Slaves 4 will be UART controller but for now - it's omitted */ assign S4_ACK_O = 1; assign S4_DAT_O = 0; assign S4_STALL_O = 0; - assign S5_ACK_O = 1; - assign S5_DAT_O = 0; - assign S5_STALL_O = 0; - intercon intercon ( .CLK(CLK), @@ -312,7 +322,6 @@ module soc assign RST = reset; assign led1 = !M_finished; - assign led2 = !M_finished; `ifdef SIMULATION /* avoid undefined values */ diff --git a/tests/soc_measure_time/Makefile b/tests/soc_measure_time/Makefile new file mode 100644 index 0000000..3f76875 --- /dev/null +++ b/tests/soc_measure_time/Makefile @@ -0,0 +1,8 @@ +DEPENDS = instructions.mem sram.v vga_display.v flash_memory.v \ + soc_with_peripherals.v ../../design/*.v messages.vh + +IVFLAGS = -DROM_WORDS_COUNT=$(call FILE_LINES,instructions.mem) + +TOP = soc_test + +include ../../Makefile.test diff --git a/tests/soc_measure_time/instructions.s.tcl b/tests/soc_measure_time/instructions.s.tcl new file mode 100644 index 0000000..b1f8511 --- /dev/null +++ b/tests/soc_measure_time/instructions.s.tcl @@ -0,0 +1,57 @@ +## also look at stack_machine_cond_jump test + +## we're going to write numbers from 0 to 639 at addresses h100000 to h1009FC +## and then write non-zero value at h100A00 + +# this will translate to 2 16-bit instructions +set_sp h100000 + +## load current value of timer, in a loop +## this is address 4 we later jump to +# this will translate to 2 16-bit instructions +loadwzx h1C0008 + +## loop until timer exceeds 500 +# this will translate to 2 16-bit instructions +const 500 +# this will translate to 1 16-bit instruction +lt +# this will translate to 1 16-bit instruction +cond_jump 4 + +## now, light led2 +# this will translate to 1 16-bit instruction +const 1 +# this will translate to 2 16-bit instructions +storew h1C0006 + +## second loop, analogous to the first one +## this is address 22 we later jump to +# this will translate to 2 16-bit instructions +loadwzx h1C0008 + +## loop until timer exceeds 1000 +# this will translate to 2 16-bit instructions +const 1000 +# this will translate to 1 16-bit instruction +lt +# this will translate to 1 16-bit instruction +cond_jump 22 + +## now, switch led2 off +# this will translate to 1 16-bit instruction +const 0 +# this will translate to 2 16-bit instructions +storew h1C0006 + +## third loop, analogous to the first two +## this is address 40 we later jump to +loadwzx h1C0008 + +## loop until timer exceeds 1500 +const 1500 +lt +cond_jump 40 + +## finish operation (will also put led1 on) +halt diff --git a/tests/soc_measure_time/test.v b/tests/soc_measure_time/test.v new file mode 100644 index 0000000..b4f1082 --- /dev/null +++ b/tests/soc_measure_time/test.v @@ -0,0 +1,82 @@ +`default_nettype none +`timescale 1ns/1ns + +`include "messages.vh" + +`ifndef SIMULATION + `error_SIMULATION_not_defined +; /* Cause syntax error */ +`endif + +module soc_test(); + wire [9:0] image_writes; + + reg clock_100mhz; + reg reset; + + wire led1; + wire led2; + + soc_with_peripherals + #( + .FONT_FILE("../../design/font.mem"), + .EMBEDDED_ROM_FILE("instructions.mem") + ) soc + ( + .clock_100mhz(clock_100mhz), + + .button1(!reset), + .button2(1'b1), + + .led1(led1), + .led2(led2), + + .image_writes(image_writes) + ); + + integer i; + integer current_time; + + initial begin + reset <= 1; + clock_100mhz <= 0; + + for (i = 0; i < 25_000; i++) begin + #5; + + if (clock_100mhz) + reset <= 0; + + clock_100mhz <= ~clock_100mhz; + + /* + * Soc's clock is 12.5 MHz. One tick of that clock takes 80 ns. + * This means 1000th, 2000th and 3000th ticks happen at + * 80_000, 160_000 and 240_000 ns, respectively. + */ + current_time = $time; + if (current_time < 40_000) begin + if (!led2) begin + `MSG(("error: led2 on before 1000 timer ticks passed")); + $finish; + end + end + if (current_time > 50_000 && current_time < 80_000) begin + if (led2) begin + `MSG(("error: led2 not on, while it should be")); + $finish; + end + end + if (current_time > 130_000) begin + if (!led2) begin + `MSG(("error: led2 hasn't been switched off on time")); + $finish; + end + end + end // for (i = 0; i < 12_500; i++) + + if (led1) + `MSG(("error: stack machine in soc hasn't finished working in 125us")); + $finish; + end // initial begin +endmodule // soc_test -- cgit v1.2.3