commit 0485c1c84594e7e1999c98cbfd58dd7f36a32763 Author: shylie Date: Sat Sep 6 11:55:53 2025 -0400 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1ac1ae5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2025 shylie + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f44757 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +YOSYS = yosys +NEXTPNR = nextpnr-ice40 +ICEPACK = icepack +DFU_UTIL = dfu-util +BIN2UF2 = bin2uf2 +IVERILOG = iverilog +VVP = vvp + +RTL = top.sv spi.sv display.sv + +all: gateware.bin iverilog.vcd + +clean: + $(RM) *.json *.asc *.bin *.uf2 *.vcd *.vvp + +prog: gateware.bin + $(DFU_UTIL) -d 1209:b1c0 -a 1 -D gateware.bin + +prog_flash: gateware.bin + $(DFU_UTIL) -d 1209:b1c0 -a 0 -D gateware.bin -R + +gateware.bin: $(RTL) + $(YOSYS) -q -p "read_verilog -sv $(RTL); synth_ice40 -top top -json $*.json" + $(NEXTPNR) -q --randomize-seed --up5k --package sg48 --pcf constraints.pcf --json $*.json --asc $*.asc + $(ICEPACK) $*.asc $@ + +iverilog.vcd: testbench.sv $(RTL) + $(IVERILOG) -g2012 -Wall -o $*vvp.vvp testbench.sv $(RTL) + $(VVP) $*vvp.vvp $@ + +.bin.uf2: + $(BIN2UF2) -o $@ $< + +.SUFFIXES: .v .sv .asc .bin .uf2 diff --git a/constraints.pcf b/constraints.pcf new file mode 100644 index 0000000..34c599c --- /dev/null +++ b/constraints.pcf @@ -0,0 +1,54 @@ +# Signals used by the default firmware +set_io -nowarn CLK 35 +set_io -nowarn UART_TX 25 +set_io -nowarn UART_RX 27 + +# Signals definition (as in the circuit diagram) +set_io -nowarn ICE_2 2 +set_io -nowarn ICE_3 3 +set_io -nowarn ICE_4 4 +set_io -nowarn ICE_6 6 +set_io -nowarn ICE_9 9 +set_io -nowarn ICE_10 10 +set_io -nowarn ICE_11 11 +set_io -nowarn ICE_18 18 +set_io -nowarn ICE_19 19 +set_io -nowarn ICE_20_G3 20 +set_io -nowarn ICE_21 21 +set_io -nowarn ICE_23 23 +set_io -nowarn ICE_25 25 +set_io -nowarn ICE_26 26 +set_io -nowarn ICE_27 27 +set_io -nowarn ICE_28 28 +set_io -nowarn ICE_31 31 +set_io -nowarn ICE_32 32 +set_io -nowarn ICE_34 34 +set_io -nowarn ICE_35_G0 35 +set_io -nowarn ICE_36 36 +set_io -nowarn ICE_38 38 +set_io -nowarn ICE_42 42 +set_io -nowarn ICE_43 43 +set_io -nowarn ICE_44_G6 44 +set_io -nowarn ICE_45 45 +set_io -nowarn ICE_46 46 +set_io -nowarn ICE_47 47 +set_io -nowarn ICE_48 48 +set_io -nowarn ICE_FLASH_IO2 12 +set_io -nowarn ICE_FLASH_IO3 13 +set_io -nowarn ICE_PB 10 # active-low +set_io -nowarn ICE_SO 14 +set_io -nowarn ICE_SI 17 +set_io -nowarn ICE_SCK 15 +set_io -nowarn ICE_SSN 16 # active-low +set_io -nowarn SRAM_SS 37 # active-high +set_io -nowarn LED_G 39 # active-low +set_io -nowarn LED_B 40 # active-low +set_io -nowarn LED_R 41 # active-low + +# Alternate names found on the silkscreen +set_io -nowarn ICE_SW2 10 # active-low + +set_io -nowarn DISPLAY_SCK 20 +set_io -nowarn DISPLAY_MOSI 26 +set_io -nowarn DISPLAY_CS 18 +set_io -nowarn DISPLAY_DC 23 diff --git a/display.sv b/display.sv new file mode 100644 index 0000000..d6a404a --- /dev/null +++ b/display.sv @@ -0,0 +1,354 @@ +`timescale 1ps / 1ps + +module display( + input wire nreset, + input wire clk, + output wire sck, + output wire cs, + output wire mosi, + output wire dc +); + +localparam STATE_INIT_WAIT = 0; +localparam STATE_INIT_RESET = 1; +localparam STATE_INIT_WAIT_AFTER_RESET = 2; +localparam STATE_INIT_STOP_SLEEP = 3; +localparam STATE_INIT_WAIT_AFTER_STOP_SLEEP = 4; +localparam STATE_INIT_SET_FORMAT = 5; +localparam STATE_INIT_SET_FORMAT_PARAM1 = 6; +localparam STATE_INIT_WAIT_AFTER_SET_FORMAT = 7; +localparam STATE_INIT_SET_COLUMN_ADDRESS_RANGE = 8; +localparam STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM1 = 9; +localparam STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM2 = 10; +localparam STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM3 = 11; +localparam STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM4 = 12; +localparam STATE_INIT_SET_ROW_ADDRESS_RANGE = 13; +localparam STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM1 = 14; +localparam STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM2 = 15; +localparam STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM3 = 16; +localparam STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM4 = 17; +localparam STATE_INIT_SET_ADDRESSING_MODE = 18; +localparam STATE_INIT_SET_ADDRESSING_MODE_PARAM1 = 19; +localparam STATE_INIT_SET_DISPLAY_INVERSION = 20; +localparam STATE_INIT_SET_DISPLAY_MODE = 21; +localparam STATE_INIT_WAIT_AFTER_SET_DISPLAY_MODE = 22; +localparam STATE_INIT_TURN_ON_DISPLAY = 23; +localparam STATE_INIT_WAIT_AFTER_TURN_ON_DISPLAY = 24; +localparam STATE_SEND_DISPLAY_CONTENTS_COMMAND = 25; +localparam STATE_SEND_DISPLAY_CONTENTS = 26; + +wire next; + +logic [7:0] spi_data_r; +logic send_data_r; +logic dc_r; + +logic [20:0] wait_counter_r; + +logic [4:0] state; + +spi spi_inst( + .nreset(nreset), + .clk(clk), + .data(spi_data_r), + .send_data(send_data_r), + .sck(sck), + .mosi(mosi), + .next(next) +); + +always_ff @(posedge clk) begin + if (nreset) begin + send_data_r <= 0; + + case (state) + STATE_INIT_WAIT : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + state <= STATE_INIT_RESET; + end + end + + STATE_INIT_RESET : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h01; + + if (next) begin + wait_counter_r <= 150000 * 12; // 150 ms wait + state <= STATE_INIT_WAIT_AFTER_RESET; + end + end + + STATE_INIT_WAIT_AFTER_RESET : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + state <= STATE_INIT_STOP_SLEEP; + end + end + + STATE_INIT_STOP_SLEEP : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h11; + + if (next) begin + wait_counter_r <= 10000 * 12; // 10 ms wait + state <= STATE_INIT_WAIT_AFTER_STOP_SLEEP; + end + end + + STATE_INIT_WAIT_AFTER_STOP_SLEEP : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + + state <= STATE_INIT_SET_FORMAT; + end + end + + STATE_INIT_SET_FORMAT : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h3A; + + if (next) begin + state <= STATE_INIT_SET_FORMAT_PARAM1; + end + end + + STATE_INIT_SET_FORMAT_PARAM1 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h66; + + if (next) begin + state <= STATE_INIT_WAIT_AFTER_SET_FORMAT; + wait_counter_r <= 10000 * 12; // 10ms wait + end + end + + STATE_INIT_WAIT_AFTER_SET_FORMAT : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + + state <= STATE_INIT_SET_COLUMN_ADDRESS_RANGE; + end + end + + STATE_INIT_SET_COLUMN_ADDRESS_RANGE : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h2A; + + if (next) begin + state <= STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM1; + end + end + + STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM1 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h00; + + if (next) begin + state <= STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM2; + end + end + + STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM2 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h35; + + if (next) begin + state <= STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM3; + end + end + + STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM3 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h00; + + if (next) begin + state <= STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM4; + end + end + + STATE_INIT_SET_COLUMN_ADDRESS_RANGE_PARAM4 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'hBB; + + if (next) begin + state <= STATE_INIT_SET_ROW_ADDRESS_RANGE; + end + end + + STATE_INIT_SET_ROW_ADDRESS_RANGE : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h2B; + + if (next) begin + state <= STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM1; + end + end + + STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM1 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h00; + + if (next) begin + state <= STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM2; + end + end + + STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM2 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h28; + + if (next) begin + state <= STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM3; + end + end + + STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM3 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h01; + + if (next) begin + state <= STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM4; + end + end + + STATE_INIT_SET_ROW_ADDRESS_RANGE_PARAM4 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'h17; + + if (next) begin + state <= STATE_INIT_SET_ADDRESSING_MODE; + end + end + + STATE_INIT_SET_ADDRESSING_MODE : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h36; + + if (next) begin + state <= STATE_INIT_SET_ADDRESSING_MODE_PARAM1; + end + end + + STATE_INIT_SET_ADDRESSING_MODE_PARAM1 : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'hC0; + + if (next) begin + state <= STATE_INIT_SET_DISPLAY_INVERSION; + end + end + + STATE_INIT_SET_DISPLAY_INVERSION : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h21; + + if (next) begin + state <= STATE_INIT_SET_DISPLAY_MODE; + end + end + + STATE_INIT_SET_DISPLAY_MODE : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h13; + + if (next) begin + wait_counter_r <= 10000 * 12; // 10ms wait + state <= STATE_INIT_WAIT_AFTER_SET_DISPLAY_MODE; + end + end + + STATE_INIT_WAIT_AFTER_SET_DISPLAY_MODE : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + + state <= STATE_INIT_TURN_ON_DISPLAY; + end + end + + STATE_INIT_TURN_ON_DISPLAY : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h29; + + if (next) begin + wait_counter_r <= 10000 * 12; // 10ms wait + state <= STATE_INIT_WAIT_AFTER_TURN_ON_DISPLAY; + end + end + + STATE_INIT_WAIT_AFTER_TURN_ON_DISPLAY : begin + wait_counter_r <= wait_counter_r - 1; + + if (wait_counter_r == 0) begin + wait_counter_r <= 0; + + state <= STATE_SEND_DISPLAY_CONTENTS_COMMAND; + end + end + + STATE_SEND_DISPLAY_CONTENTS_COMMAND : begin + send_data_r <= 1; + dc_r <= 0; + spi_data_r <= 8'h2C; + + if (next) begin + wait_counter_r <= 97200; // 240 * 135 * 3 + state <= STATE_SEND_DISPLAY_CONTENTS; + end + end + + STATE_SEND_DISPLAY_CONTENTS : begin + send_data_r <= 1; + dc_r <= 1; + spi_data_r <= 8'b11111100; + + if (next) begin + wait_counter_r <= wait_counter_r - 1; + if (wait_counter_r == 1) begin + state <= STATE_SEND_DISPLAY_CONTENTS_COMMAND; + end + end + end + endcase + end else begin + spi_data_r <= 0; + send_data_r <= 0; + wait_counter_r <= 10; + dc_r <= 0; + state <= STATE_INIT_WAIT; + end +end + +assign cs = ~send_data_r; +assign dc = dc_r; + +endmodule diff --git a/testbench.sv b/testbench.sv new file mode 100644 index 0000000..ccd0bc2 --- /dev/null +++ b/testbench.sv @@ -0,0 +1,40 @@ +`timescale 1ps / 1ps + +module testbench; + +reg clk; +wire r; +wire g; +wire b; +wire sck; +wire mosi; +wire cs; +wire dc; + +top t( + .CLK(clk), + .LED_R(r), + .LED_G(g), + .LED_B(b), + .DISPLAY_SCK(sck), + .DISPLAY_MOSI(mosi), + .DISPLAY_CS(cs), + .DISPLAY_DC(dc) +); + +initial begin + $dumpfile("iverilog.vcd"); + $dumpvars(0, testbench); + + clk = 0; + + #10000000 + $finish; +end + +always begin + #1 clk = ~clk; +end + + +endmodule diff --git a/top.sv b/top.sv new file mode 100644 index 0000000..e915989 --- /dev/null +++ b/top.sv @@ -0,0 +1,43 @@ +`timescale 1ps / 1ps + +module top( + input CLK, + output LED_R, + output LED_G, + output LED_B, + output DISPLAY_SCK, + output DISPLAY_MOSI, + output DISPLAY_CS, + output DISPLAY_DC +); + +localparam N = 18; + +logic [N:0] counter; + +logic nreset_r = 0; + +display display_inst( + .nreset(nreset_r), + .clk(CLK), + .sck(DISPLAY_SCK), + .cs(DISPLAY_CS), + .mosi(DISPLAY_MOSI), + .dc(DISPLAY_DC) +); + +always_ff @(posedge CLK) begin + if (nreset_r) begin + counter <= counter + 1; + end else begin + counter <= '0; + + nreset_r <= 1; + end +end + +assign LED_R = 1'b1; +assign LED_G = counter[N]; +assign LED_B = 1'b1; + +endmodule