Initial commit

This commit is contained in:
shylie 2025-09-06 11:55:53 -04:00
commit 0485c1c845
6 changed files with 546 additions and 0 deletions

21
LICENSE Normal file
View File

@ -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.

34
Makefile Normal file
View File

@ -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

54
constraints.pcf Normal file
View File

@ -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

354
display.sv Normal file
View File

@ -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

40
testbench.sv Normal file
View File

@ -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

43
top.sv Normal file
View File

@ -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