diff --git a/hdl/pattern/moving_pix.sv b/hdl/pattern/moving_pix.sv new file mode 100644 index 0000000..9333899 --- /dev/null +++ b/hdl/pattern/moving_pix.sv @@ -0,0 +1,77 @@ +`timescale 1ns / 1ps +`include "common/evt_counter.sv" +`default_nettype none // prevents system from inferring an undeclared logic (good practice) + + +module moving_pix #( + parameter int CLOCK_SPEED = 100_000_000, // 100MHz + parameter int NUM_LEDS = 20, // !!! ASSUMES STRAND DRIVER HAS MORE LEDS (next_led_request >= NUM_LEDS) + parameter int FRAMES_PER_LED = 100, + parameter int COLOR_WIDTH = 8, + parameter int GREEN_VAL = 8'hFF, + parameter int RED_VAL = 8'hFF, + parameter int BLUE_VAL = 8'hFF, + localparam int LED_COUNTER_WIDTH = $clog2(NUM_LEDS), + localparam int FRAME_COUNTER_WIDTH = $clog2(FRAMES_PER_LED) + ) + ( + input wire rst_in, // active high + input wire clk_in, // 100MHz + input wire [LED_COUNTER_WIDTH-1:0] next_led_request, + input wire request_valid, + output logic [COLOR_WIDTH-1:0] green_out, + output logic [COLOR_WIDTH-1:0] red_out, + output logic [COLOR_WIDTH-1:0] blue_out, + output logic color_ready + ); + + logic [LED_COUNTER_WIDTH-1:0] current_pixel_idx; + logic [LED_COUNTER_WIDTH-1:0] last_pixel_request; + logic [FRAME_COUNTER_WIDTH-1:0] num_strand_frames; + logic cur_pix_displayed; + assign cur_pix_displayed = ((last_pixel_request != next_led_request) && (last_pixel_request == current_pixel_idx)); + logic displayed_prev_led; + + always_ff @(posedge clk_in) begin + if (rst_in) begin + // Reset all states + current_pixel_idx <= 0; + last_pixel_request <= next_led_request; + num_strand_frames <= 0; + green_out <= 8'h00; + red_out <= 8'h00; + blue_out <= 8'h00; + color_ready <= 0; + displayed_prev_led <= 0; + end else begin + // Update num_strand_frames too keep track of how many frames we display current_pixel_idx + last_pixel_request <= next_led_request; + if (cur_pix_displayed) begin + if (num_strand_frames == FRAMES_PER_LED - 1) begin + // Increment current pixel index to display + current_pixel_idx <= (current_pixel_idx == NUM_LEDS - 1) ? 0 : current_pixel_idx + 1; + displayed_prev_led <= 1; + end else begin + displayed_prev_led <= 0; + end + // handle looping and skipping next led on last frame of current led + num_strand_frames <= (displayed_prev_led) ? 0 : (num_strand_frames == FRAMES_PER_LED - 1) ? 0 : num_strand_frames + 1; + end + + // Respond to requests + if (next_led_request == current_pixel_idx && !displayed_prev_led) begin + green_out <= GREEN_VAL; + red_out <= RED_VAL; + blue_out <= BLUE_VAL; + color_ready <= 1; + end else begin + green_out <= 8'h00; + red_out <= 8'h0A; + blue_out <= 8'h00; + color_ready <= 1; + end + end + end + +endmodule +`default_nettype wire \ No newline at end of file diff --git a/hdl/top_level.sv b/hdl/top_level.sv index ca1921a..7e819da 100644 --- a/hdl/top_level.sv +++ b/hdl/top_level.sv @@ -1,6 +1,7 @@ `timescale 1ns / 1ps // (comment to prevent autoformatting) `include "driver/led_driver.sv" `include "pattern/pat_gradient.sv" +`include "pattern/moving_pix.sv" `include "clk/cw_hdmi_clk_wiz.v" `include "clk/cw_fast_clk_wiz.v" `include "cam/camera_reader.sv" @@ -17,10 +18,11 @@ `default_nettype none module top_level #( - parameter int NUM_LEDS = 10, + parameter int NUM_LEDS = 50, parameter int COLOR_WIDTH = 8, localparam int CounterWidth = $clog2(NUM_LEDS) ) ( + input wire clk_100mhz, output logic [15:0] led, // camera bus @@ -53,17 +55,31 @@ module top_level #( logic [CounterWidth-1:0] next_led_request; // instantiate pattern modules - pat_gradient #( + // pat_gradient #( + // .NUM_LEDS(NUM_LEDS), + // .COLOR_WIDTH(COLOR_WIDTH) + // ) pat_gradient_inst ( + // .rst_in(sys_rst_led), + // .clk_in(clk_100_passthrough), + // .next_led_request(next_led_request), + // .red_out(next_red), + // .green_out(next_green), + // .blue_out(next_blue), + // .color_valid(color_valid) + // ); + // instantiate moving_pix module + moving_pix #( .NUM_LEDS(NUM_LEDS), .COLOR_WIDTH(COLOR_WIDTH) ) pat_gradient_inst ( .rst_in(sys_rst_led), .clk_in(clk_100_passthrough), .next_led_request(next_led_request), - .red_out(next_red), + .request_valid(1), .green_out(next_green), + .red_out(next_red), .blue_out(next_blue), - .color_valid(color_valid) + .color_ready(color_valid) ); // instantiate led_driver module diff --git a/sim/test_moving_pix.py b/sim/test_moving_pix.py new file mode 100644 index 0000000..7ec7731 --- /dev/null +++ b/sim/test_moving_pix.py @@ -0,0 +1,86 @@ +import os +import sys +from pathlib import Path +import typing +from collections import deque + +import cocotb +from cocotb.clock import Clock +from cocotb.runner import get_runner +from cocotb.triggers import ClockCycles, FallingEdge, RisingEdge + +NUM_LEDS = 5 +NUM_FRAMES_PER_LED = 4 + + +@cocotb.test() +async def test_a(dut): + """Test for driving first pixel a correct color""" + dut._log.info("Starting...") + cocotb.start_soon(Clock(dut.clk_in, 10, units="ns").start()) + + await ClockCycles(dut.clk_in, 2) # check the pre-reset behavior + + # Reset + dut.rst_in.value = 1 + await ClockCycles(dut.clk_in, 3) + await FallingEdge(dut.clk_in) + dut.rst_in.value = 0 + dut.next_led_request.value = 0 + + # Start Driving + for pix in range(NUM_LEDS): + for frame in range(NUM_FRAMES_PER_LED): + for request in range(NUM_LEDS): + # Iterate through the strand + await RisingEdge(dut.clk_in) + dut.next_led_request.value = request + await FallingEdge(dut.clk_in) + await ClockCycles(dut.clk_in, 120) + dut._log.info(f"Checking pixel {pix} frame {frame}") + dut._log.info(f"g: {dut.green_out.value}, r: {dut.red_out.value}, b: {dut.blue_out.value}") + if pix == request: + assert dut.green_out.value == 0xFF + assert dut.red_out.value == 0xFF + assert dut.blue_out.value == 0xFF + else: + assert dut.green_out.value == 0 + assert dut.red_out.value == 0 + assert dut.blue_out.value == 0 + + +def is_runner(): + """Moving pixel tester""" + hdl_toplevel_lang = os.getenv("HDL_TOPLEVEL_LANG", "verilog") + sim = os.getenv("SIM", "icarus") + proj_path = Path(__file__).resolve().parent.parent + sys.path.append(str(proj_path / "sim" / "model")) + sources = [proj_path / "hdl" / "pattern" / "moving_pix.sv"] + build_test_args = ["-Wall"] + parameters = { + "NUM_LEDS": NUM_LEDS, + "FRAMES_PER_LED": NUM_FRAMES_PER_LED, + } + sys.path.append(str(proj_path / "sim")) + runner = get_runner(sim) + runner.build( + sources=sources, + hdl_toplevel="moving_pix", + includes=[proj_path / "hdl"], + always=True, + build_args=build_test_args, + parameters=parameters, + timescale=("1ns", "1ps"), + waves=True, + ) + run_test_args = [] + runner.test( + hdl_toplevel="moving_pix", + test_module="test_moving_pix", + test_args=run_test_args, + waves=True, + ) + + +if __name__ == "__main__": + is_runner()