Initial commit

This commit is contained in:
Nikolay Puzanov 2021-02-28 18:59:56 +03:00
parent ff795d2b6e
commit 2fbacc0544
61 changed files with 6063 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
build
*~
GPATH
GRTAGS
GTAGS
TAGS

56
Makefile Normal file
View File

@ -0,0 +1,56 @@
SOURCES = $(wildcard source/*.sv)
TOP = sugar_lissajous
PCF = icesugar.pcf
FAMILY = up5k
PACKAGE = sg48
FREQ = 30
#DOSVG = --placed-svg place.svg --routed-svg route.svg
# nextpnr --randomize-seed write_verilog $(TOP).v
BUILD_DIR ?= build
TOP_BIN = $(BUILD_DIR)/$(TOP).bin
TOP_ASC = $(BUILD_DIR)/$(TOP).asc
TOP_JSON = $(BUILD_DIR)/$(TOP).json
all: $(TOP_BIN)
# Make bitstream
$(TOP_BIN): $(TOP_ASC)
icepack $(TOP_ASC) $(TOP_BIN)
# Place and rouite
$(TOP_ASC): $(TOP_JSON) $(PCF)
nextpnr-ice40 -q -l $(BUILD_DIR)/nextpnr.log --$(FAMILY) --package $(PACKAGE) \
--freq $(FREQ) --top $(TOP) --pcf $(PCF) --asc $(TOP_ASC) --json $(TOP_JSON) \
$(DOSVG)
# Synthesys
$(TOP_JSON): $(SOURCES)
mkdir -p $(BUILD_DIR)
yosys -q -l $(BUILD_DIR)/yosys.log -p \
"proc; alumacc; share -fast; opt -full; synth_ice40 -top $(TOP) -json $(TOP_JSON) -abc2" \
$(SOURCES)
# Timing analysis
timing: $(TOP_ASC)
icetime -d $(FAMILY) -t -c $(FREQ) -r $(BUILD_DIR)/timing.log $(TOP_ASC)
# Program
prog: $(TOP_BIN)
icesprog -w $(TOP_BIN)
# Clean
clean:
rm -rf $(BUILD_DIR)
# Convert SVG to PNG
png: route.png place.png
route.png: route.svg
inkscape --export-type=png -o route.png -D -d 100 route.svg
place.png: place.svg
inkscape --export-type=png -o place.png -D -d 150 place.svg

16
README.md Normal file
View File

@ -0,0 +1,16 @@
Light-organ based on [iCESugar 1.5](https://github.com/wuxx/icesugar)
board (Lattice iCE40UP5k), nameless SPI TFT LCD display from AliExpress,
and sound capture board [Dual MCP3102](https://github.com/punzik/dual-mcp3201-pmod).
Project is synthesized by [Yosys](https://github.com/YosysHQ/yosys), routed and
placed by [nextpnr](https://github.com/YosysHQ/nextpnr), verefied by
[iverilog](https://github.com/steveicarus/iverilog) and
[verilator](https://github.com/verilator/verilator). For scripting, prototyping and
GUI use [Racket](https://racket-lang.org/) and [GNU
Octave](https://www.gnu.org/software/octave/index).
Video 1: https://youtu.be/R9meEMrbPAM
Video 2: https://youtu.be/H2083E0BFIM
![Photo](photo.jpg)

BIN
doc/ILI9341 Datasheet.pdf Normal file

Binary file not shown.

61
icesugar.pcf Normal file
View File

@ -0,0 +1,61 @@
# iCESugar Board (iCE40UP5K-QFN48)
set_io -nowarn LED_R_N 39
set_io -nowarn LED_G_N 40
set_io -nowarn LED_B_N 41
set_io -nowarn SW[0] 18
set_io -nowarn SW[1] 19
set_io -nowarn SW[2] 20
set_io -nowarn SW[3] 21
set_io -nowarn CLK12 35
set_io -nowarn UART_RX 4
set_io -nowarn UART_TX 6
set_io -nowarn USB_DP 10
set_io -nowarn USB_DN 9
set_io -nowarn USB_PUP 11
# PMOD 1
set_io -nowarn P1_1 10
set_io -nowarn P1_2 6
set_io -nowarn P1_3 3
set_io -nowarn P1_4 48
set_io -nowarn P1_9 47
set_io -nowarn P1_10 2
set_io -nowarn P1_11 4
set_io -nowarn P1_12 9
# PMOD 2
set_io -nowarn P2_1 46
set_io -nowarn P2_2 44
set_io -nowarn P2_3 42
set_io -nowarn P2_4 37
set_io -nowarn P2_9 36
set_io -nowarn P2_10 38
set_io -nowarn P2_11 43
set_io -nowarn P2_12 45
# PMOD 3
set_io -nowarn P3_1 34
set_io -nowarn P3_2 31
set_io -nowarn P3_3 27
set_io -nowarn P3_4 25
set_io -nowarn P3_9 23
set_io -nowarn P3_10 26
set_io -nowarn P3_11 28
set_io -nowarn P3_12 32
# PMOD 4
set_io -nowarn P4_1 21
set_io -nowarn P4_2 20
set_io -nowarn P4_3 19
set_io -nowarn P4_4 18
#spi
set_io -nowarn SPI_SS 16
set_io -nowarn SPI_SCK 15
set_io -nowarn SPI_MOSI 17
set_io -nowarn SPI_MISO 14

BIN
photo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 KiB

5
source/.dir-locals.el Normal file
View File

@ -0,0 +1,5 @@
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((verilog-mode . ((flycheck-verilator-include-path . ("." "../../local/share/yosys/ice40"))
(verilog-library-directories . ("." "../../local/share/yosys/ice40")))))

9
source/assert.vh Normal file
View File

@ -0,0 +1,9 @@
`ifndef _ASSERT_VH_
`define _ASSERT_VH_
`define assert(assertion) \
if (!(assertion)) begin \
$error("ERROR: Assertion failed in %m: assertion");\
end
`endif

29
source/circle-table.rkt Normal file
View File

@ -0,0 +1,29 @@
#lang racket
(define (gen-circle-quadrant len)
(map (lambda (n)
(let ((angle (/ (* 2 pi n) len)))
(cons (sin angle)
(cos angle))))
(range (/ len 4))))
(define (print-xy-table table)
(define (->hex x)
(~a (number->string (inexact->exact (round x)) 16)
#:min-width 2 #:align 'right #:pad-string "0"))
(for-each
(lambda (c)
(printf "~a~a\n"
(->hex (* 255 (car c)))
(->hex (* 255 (cdr c)))))
table))
;;; MAIN
(let ((x0 0)
(y0 0)
(len 1024))
(with-output-to-file (format "quadrant_~a.rom" (/ len 4))
(lambda ()
(print-xy-table
(gen-circle-quadrant len)))))

82
source/circle_1024.sv Normal file
View File

@ -0,0 +1,82 @@
`timescale 1ns/100ps
`default_nettype none
module circle_1024
(input wire clock,
input wire reset,
input wire [9:0] angle,
input wire [7:0] r,
input wire [7:0] x0,
input wire [7:0] y0,
output wire [7:0] x,
output wire [7:0] y,
input wire req_i,
output reg ack_o);
localparam QUADR_LEN = 256;
localparam QUADR_ROM_FILE = "quadrant_256.rom";
localparam QUADR_CW = $clog2(QUADR_LEN);
logic [15:0] q_rom[QUADR_LEN];
initial $readmemh(QUADR_ROM_FILE, q_rom, 0, QUADR_LEN-1);
logic [QUADR_CW-1:0] q_addr;
logic [7:0] kx, ky;
always_ff @ (posedge clock)
{kx, ky} <= q_rom[q_addr];
logic [15:0] macx_o, macy_o;
logic xsub, ysub;
ice40_2mac8x8 circle_mac
(.clock, .reset,
.a0(r),
.b0(kx),
.s0({x0, 8'b0}),
.sub0(xsub),
.y0(macx_o),
.a1(r),
.b1(ky),
.s1({y0, 8'b0}),
.sub1(ysub),
.y1(macy_o));
assign q_addr = angle[8] ? 8'd255 - angle[7:0] : angle[7:0];
assign xsub = angle[9];
assign ysub = angle[8] == angle[9] ? 1'b0 : 1'b1;
assign x = macx_o[15:8];
assign y = macy_o[15:8];
enum int unsigned {
ST_IDLE = 0,
ST_GET_K,
ST_MAC
} state;
always_ff @(posedge clock, posedge reset)
if (reset) begin
state <= ST_IDLE;
ack_o <= 1'b0;
end
else
case (state)
ST_IDLE:
if (req_i)
state <= ST_GET_K;
ST_GET_K: begin
ack_o <= 1'b1;
state <= ST_MAC;
end
ST_MAC: begin
ack_o <= 1'b0;
state <= ST_IDLE;
end
endcase
endmodule // circle

View File

@ -0,0 +1,8 @@
00 00 3f 3f 3f 3f 00 00
00 3f 7f 7f 7f 7f 3f 00
3f 7f bf bf bf bf 7f 3f
3f 7f bf ff ff bf 7f 3f
3f 7f bf ff ff bf 7f 3f
3f 7f bf bf bf bf 7f 3f
00 3f 7f 7f 7f 7f 3f 00
00 00 3f 3f 3f 3f 00 00

223
source/fig_drawer.sv Normal file
View File

@ -0,0 +1,223 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
module fig_drawer #(parameter FIG_W = 8,
parameter FIG_H = 8,
parameter FIG_ROM_FILE = "fig_circle_8x8.rom")
(input wire clock,
input wire reset,
input wire [7:0] x_i,
input wire [8:0] y_i,
input wire [7:0] h_i,
input wire [7:0] s_i,
input wire [7:0] v_i,
input wire req_i,
output reg ack_o,
output reg [7:0] fb_x_o,
output reg [8:0] fb_y_o,
output reg [15:0] fb_color_o,
output reg fb_req_o,
input wire fb_ack_i);
initial begin
`assert(FIG_W > 0);
`assert(FIG_H > 0);
/* Check power of 2 */
`assert(FIG_W == 1 << $clog2(FIG_W));
`assert(FIG_H == 1 << $clog2(FIG_H));
end
/* Figure bitmap */
localparam FIG_SIZE = FIG_W * FIG_H;
localparam FIG_W_CW = $clog2(FIG_W);
localparam FIG_H_CW = $clog2(FIG_H);
localparam FIG_CW = $clog2(FIG_SIZE);
logic [7:0] fig[FIG_SIZE];
initial $readmemh(FIG_ROM_FILE, fig, 0, FIG_SIZE-1);
logic [FIG_CW-1:0] fig_addr;
logic [7:0] fig_data;
always_ff @ (posedge clock)
fig_data <= fig[fig_addr];
/* Scale brightness */
logic [31:0] mac_v_o;
logic [7:0] fig_v;
assign fig_v = mac_v_o[15:8];
ice40_mac16x16 mac_v
(.clock, .reset,
.a({8'b0, fig_data}),
.b({8'b0, v_i}),
.s(32'b0),
.sub(1'b0),
.y(mac_v_o));
/* Convert HSV ro RGB */
logic hsv_ready;
logic rgb_valid;
logic [7:0] r, g, b;
hsv2rgb hsv2rgb_i
(.clock, .reset,
.h(h_i), .s(s_i), .v(fig_v),
.ready_i(hsv_ready),
.r(r), .g(g), .b(b),
.valid_o(rgb_valid));
/* FSM states */
enum int unsigned {
ST_IDLE = 0, // 0
ST_READ_PIX, // 1
ST_MULT_V, // 2
ST_HSV_READY, // 3
ST_RGB_WAIT, // 4
ST_WAIT_FB, // 5
ST_NEXT_PIXEL, // 6
ST_DONE // 7
} state, next;
/* FSM sync part */
always_ff @(posedge clock, posedge reset)
if (reset) state <= ST_IDLE;
else state <= next;
/* Pixel coordinate */
logic [FIG_W_CW-1:0] pix_x;
logic [FIG_H_CW-1:0] pix_y;
logic pix_reset, pix_next;
assign fig_addr = {pix_y, pix_x};
always_ff @(posedge clock)
if (pix_reset) begin
pix_x <= '0;
pix_y <= '0;
end
else
if (pix_next)
if (pix_x == FIG_H_CW'(FIG_H-1)) begin
pix_x <= '0;
if (pix_y == FIG_W_CW'(FIG_W-1))
pix_y <= '0;
else
pix_y <= pix_y + 1'b1;
end
else
pix_x <= pix_x + 1'b1;
/* Frame buffer control */
logic [7:0] fb_x;
logic [8:0] fb_y;
logic [15:0] fb_color;
logic fb_hold, fb_restore;
logic fb_req;
always_ff @ (posedge clock, posedge reset)
if (reset)
fb_req_o <= 1'b0;
else
if (fb_req)
fb_req_o <= 1'b1;
else
if (fb_ack_i)
fb_req_o <= 1'b0;
assign fb_x = x_i + 8'(pix_x);
assign fb_y = y_i + 9'(pix_y);
always_ff @ (posedge clock) begin
if (rgb_valid) begin
fb_color <= {r[7:3], g[7:2], b[7:3]};
if (~fb_hold)
fb_color_o <= {r[7:3], g[7:2], b[7:3]};
end
if (fb_restore)
fb_color_o <= fb_color;
end
always_ff @ (posedge clock)
if (fb_req) begin
fb_x_o <= fb_x;
fb_y_o <= fb_y;
end
/* FSM comb part */
always_comb begin
next = state;
pix_reset = 1'b0;
pix_next = 1'b0;
fb_req = 1'b0;
fb_hold = 1'b0;
fb_restore = 1'b0;
ack_o = 1'b0;
hsv_ready = 1'b0;
case (state)
ST_IDLE:
if (req_i) begin
pix_reset = 1'b1;
next = ST_READ_PIX;
end
ST_READ_PIX:
next = ST_MULT_V;
ST_MULT_V:
if (fig_data == '0) begin
pix_next = 1'b1;
next = ST_NEXT_PIXEL;
end
else
next = ST_HSV_READY;
ST_HSV_READY: begin
hsv_ready = 1'b1;
next = ST_RGB_WAIT;
end
ST_RGB_WAIT:
if (rgb_valid) begin
if (fb_req_o && !fb_ack_i) begin
fb_hold = 1'b1;
next = ST_WAIT_FB;
end
else begin
fb_req = 1'b1;
pix_next = 1'b1;
next = ST_NEXT_PIXEL;
end
end
ST_WAIT_FB:
if (fb_ack_i) begin
fb_restore = 1'b1;
fb_req = 1'b1;
pix_next = 1'b1;
next = ST_NEXT_PIXEL;
end
ST_NEXT_PIXEL:
if (pix_x == '0 && pix_y == '0)
next = ST_DONE;
else
next = ST_MULT_V;
ST_DONE: begin
ack_o = 1'b1;
next = ST_IDLE;
end
endcase
end
endmodule // fig_drawer

168
source/fig_ring.sv Normal file
View File

@ -0,0 +1,168 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
module fig_ring #(parameter POINT_COUNT = 32,
parameter FADING = 1)
(input wire clock,
input wire reset,
input wire [7:0] pt_x,
input wire [7:0] pt_y,
input wire [7:0] pt_h,
input wire pt_req_i,
output reg pt_ack_o,
output reg [7:0] fig_x_o,
output reg [8:0] fig_y_o,
output reg [7:0] fig_h_o,
output reg [7:0] fig_s_o,
output reg [7:0] fig_v_o,
output reg fig_req_o,
input wire fig_ack_i);
initial begin
`assert(POINT_COUNT > 0);
`assert(POINT_COUNT == (1 << $clog2(POINT_COUNT)));
end
/* Points coordinate RAM */
localparam POINT_CW = $clog2(POINT_COUNT);
localparam [7:0] V_INC = FADING == 0 ? 8'd255 : 8'('h100 / POINT_COUNT);
logic [23:0] pt_ram[POINT_COUNT];
logic [POINT_CW-1:0] pt_raddr;
logic [POINT_CW-1:0] pt_waddr;
logic pt_wr;
logic [7:0] pt_xw, pt_xr;
logic [7:0] pt_yw, pt_yr;
logic [7:0] pt_hw, pt_hr;
always_ff @ (posedge clock) begin
if (pt_wr)
pt_ram[pt_waddr] <= {pt_hw, pt_xw, pt_yw};
{pt_hr, pt_xr, pt_yr} <= pt_ram[pt_raddr];
end
`ifdef TESTBENCH
integer i;
initial
for (i = 0; i < POINT_COUNT; i ++)
pt_ram[i] = {8'(i*5), 8'(i*5 + 20), 8'(i*5 + 30)};
`endif
/* FSM */
enum int unsigned {
ST_IDLE = 0, // 0
ST_READ_LAST_PT, // 1
ST_STORE_LAST_PT, // 2
ST_WRITE_NEW_PT, // 3
ST_DRAW_LAST, // 4
ST_FIG_DRAW, // 5
ST_NEXT_PT, // 6
ST_READ_PT, // 7
ST_STORE_PT, // 8
ST_ACK // 9
} state;
logic [POINT_CW-1:0] pt_last;
assign pt_xw = pt_x;
assign pt_yw = pt_y;
assign pt_hw = pt_h;
always_ff @ (posedge clock, posedge reset)
if (reset) begin
state <= ST_IDLE;
pt_wr <= 1'b0;
pt_last <= '0;
pt_ack_o <= 1'b0;
fig_req_o <= 1'b0;
end
else
case (state)
ST_IDLE: begin
if (pt_req_i) begin
pt_raddr <= pt_last;
pt_waddr <= pt_last;
pt_wr <= 1'b0;
fig_v_o <= '0;
state <= ST_READ_LAST_PT;
end
end
ST_READ_LAST_PT:
state <= ST_STORE_LAST_PT;
ST_STORE_LAST_PT: begin
fig_x_o <= pt_xr;
fig_y_o <= {1'b0, pt_yr};
fig_h_o <= pt_hr;
fig_s_o <= 8'hff; // TODO: add saturation to ring mem
pt_wr <= 1'b1;
state <= ST_WRITE_NEW_PT;
end
ST_WRITE_NEW_PT: begin
pt_wr <= 1'b0;
fig_req_o <= 1'b1;
pt_raddr <= pt_last + 1'b1;
state <= ST_DRAW_LAST;
end
ST_DRAW_LAST:
if (fig_ack_i) begin
fig_req_o <= 1'b0;
state <= ST_READ_PT;
end
ST_FIG_DRAW: begin
fig_req_o <= 1'b1;
state <= ST_NEXT_PT;
end
ST_NEXT_PT:
if (fig_ack_i) begin
fig_req_o <= 1'b0;
if (pt_raddr == pt_last) begin
pt_last <= pt_last + 1'b1;
pt_ack_o <= 1'b1;
state <= ST_ACK;
end
else begin
if (pt_raddr == POINT_CW'(POINT_COUNT-1))
pt_raddr <= '0;
else
pt_raddr <= pt_raddr + 1'b1;
state <= ST_READ_PT;
end
end
ST_READ_PT:
state <= ST_STORE_PT;
ST_STORE_PT: begin
fig_x_o <= pt_xr;
fig_y_o <= {1'b0, pt_yr};
fig_h_o <= pt_hr;
fig_s_o <= 8'hff; // TODO: add saturation to ring mem
if (fig_v_o > (255-V_INC))
fig_v_o <= 8'hff;
else
fig_v_o <= fig_v_o + V_INC;
state <= ST_FIG_DRAW;
end
ST_ACK: begin
pt_ack_o <= 1'b0;
state <= ST_IDLE;
end
endcase
endmodule // fig_ring

64
source/fir-filter.m Normal file
View File

@ -0,0 +1,64 @@
pkg load signal
%% filter length
flen = 511;
%% window
#win = ones(flen,1);
#win = chebwin(flen);
#win = blackmanharris(flen);
win = flattopwin(flen);
%% data width
bits = 16;
%% sampling frequency
fs = 20000
%% cutoff frequency
fc = 50;
%% stopband frequency
fb = 100;
%% amplification (dB)
amp_db = 10;
%% stopband attenuation (dB)
stop_db = -40;
%% filter
amp_k = 10^(amp_db/20);
stop_k = 10^((stop_db+amp_k)/20);
f = [0 fc/fs fb/fs 1];
m = [amp_k amp_k stop_k stop_k];
fcoeff = firls(flen-1, f, m) .* win;
%% scale and round to 16 bit
fcoeff_i16 = round(fcoeff .* 65536);
%% remove leading and trailing zeroes
fcoeff_i16_ms = fcoeff_i16(find(fcoeff_i16,1,'first'):find(fcoeff_i16,1,'last'));
function ret = to_hex(x)
if (x < 0)
ret = 65536+x;
else
ret = x;
endif
endfunction
%% convert negative coeffs to 16-bit two's complement
fcoeff_i16_hex = arrayfun(@to_hex, fcoeff_i16_ms);
%% printf coeffs to file
filename = sprintf("fir_%d_%dhz_%dhz_%ddb_%ddb.rom", length(fcoeff_i16_ms), fc, fb, amp_db, -stop_db);
printf("Write %d coefficients to file %s\n", length(fcoeff_i16_ms), filename);
file = fopen(filename, "w");
fprintf(file, "%04x\n", fcoeff_i16_hex);
fclose(file);
%% make real coeffs for freqz
fcoeff_i = fcoeff_i16_ms ./ 65536;
freqz(fcoeff_i);

View File

@ -0,0 +1,363 @@
ffff
ffff
ffff
ffff
ffff
ffff
ffff
fffe
fffe
fffe
fffe
fffe
fffd
fffd
fffd
fffd
fffd
fffc
fffc
fffc
fffc
fffc
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffa
fffa
fffa
fffa
fffa
fffa
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffc
fffc
fffc
fffc
fffd
fffd
fffd
fffe
fffe
fffe
ffff
ffff
ffff
0000
0000
0000
0001
0001
0001
0001
0001
0001
0001
0001
0001
0001
0000
0000
ffff
ffff
fffe
fffd
fffd
fffc
fffb
fff9
fff8
fff7
fff5
fff4
fff2
fff1
ffef
ffed
ffeb
ffe9
ffe7
ffe6
ffe4
ffe2
ffe0
ffde
ffdc
ffda
ffd9
ffd7
ffd6
ffd5
ffd4
ffd3
ffd2
ffd2
ffd2
ffd2
ffd3
ffd4
ffd6
ffd8
ffda
ffdd
ffe0
ffe4
ffe9
ffee
fff3
fff9
0000
0008
0010
0019
0023
002d
0038
0044
0051
005e
006c
007b
008b
009b
00ac
00be
00d0
00e3
00f7
010b
0120
0136
014c
0162
0179
0190
01a8
01c0
01d8
01f1
0209
0222
023b
0253
026c
0284
029c
02b4
02cc
02e3
02fa
0311
0326
033b
0350
0364
0377
0389
039a
03aa
03b9
03c7
03d4
03e0
03eb
03f5
03fd
0404
040a
040f
0412
0414
06f4
0414
0412
040f
040a
0404
03fd
03f5
03eb
03e0
03d4
03c7
03b9
03aa
039a
0389
0377
0364
0350
033b
0326
0311
02fa
02e3
02cc
02b4
029c
0284
026c
0253
023b
0222
0209
01f1
01d8
01c0
01a8
0190
0179
0162
014c
0136
0120
010b
00f7
00e3
00d0
00be
00ac
009b
008b
007b
006c
005e
0051
0044
0038
002d
0023
0019
0010
0008
0000
fff9
fff3
ffee
ffe9
ffe4
ffe0
ffdd
ffda
ffd8
ffd6
ffd4
ffd3
ffd2
ffd2
ffd2
ffd2
ffd3
ffd4
ffd5
ffd6
ffd7
ffd9
ffda
ffdc
ffde
ffe0
ffe2
ffe4
ffe6
ffe7
ffe9
ffeb
ffed
ffef
fff1
fff2
fff4
fff5
fff7
fff8
fff9
fffb
fffc
fffd
fffd
fffe
ffff
ffff
0000
0000
0001
0001
0001
0001
0001
0001
0001
0001
0001
0001
0000
0000
0000
ffff
ffff
ffff
fffe
fffe
fffe
fffd
fffd
fffd
fffc
fffc
fffc
fffc
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffa
fffa
fffa
fffa
fffa
fffa
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffb
fffc
fffc
fffc
fffc
fffc
fffd
fffd
fffd
fffd
fffd
fffe
fffe
fffe
fffe
fffe
ffff
ffff
ffff
ffff
ffff
ffff
ffff

View File

@ -0,0 +1,425 @@
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffd
fffd
fffd
fffd
fffd
fffd
fffd
fffc
fffc
fffc
fffc
fffc
fffc
fffb
fffb
fffb
fffb
fffb
fffb
fffa
fffa
fffa
fffa
fffa
fff9
fff9
fff9
fff9
fff9
fff9
fff8
fff8
fff8
fff8
fff8
fff8
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff8
fff8
fff8
fff8
fff9
fff9
fff9
fffa
fffa
fffb
fffb
fffc
fffd
fffd
fffe
ffff
ffff
0000
0001
0002
0003
0004
0005
0006
0007
0008
0009
000b
000c
000d
000f
0010
0012
0013
0015
0017
0018
001a
001c
001e
0020
0022
0024
0026
0028
002a
002d
002f
0031
0034
0036
0039
003b
003e
0040
0043
0046
0048
004b
004e
0051
0054
0057
0059
005c
005f
0062
0065
0068
006c
006f
0072
0075
0078
007b
007e
0081
0084
0088
008b
008e
0091
0094
0097
009a
009d
00a0
00a3
00a6
00a9
00ac
00af
00b2
00b4
00b7
00ba
00bc
00bf
00c1
00c4
00c6
00c9
00cb
00cd
00cf
00d1
00d3
00d5
00d7
00d9
00db
00dc
00de
00df
00e1
00e2
00e3
00e4
00e5
00e6
00e7
00e8
00e8
00e9
00e9
00e9
00ea
00ea
03c9
00ea
00ea
00e9
00e9
00e9
00e8
00e8
00e7
00e6
00e5
00e4
00e3
00e2
00e1
00df
00de
00dc
00db
00d9
00d7
00d5
00d3
00d1
00cf
00cd
00cb
00c9
00c6
00c4
00c1
00bf
00bc
00ba
00b7
00b4
00b2
00af
00ac
00a9
00a6
00a3
00a0
009d
009a
0097
0094
0091
008e
008b
0088
0084
0081
007e
007b
0078
0075
0072
006f
006c
0068
0065
0062
005f
005c
0059
0057
0054
0051
004e
004b
0048
0046
0043
0040
003e
003b
0039
0036
0034
0031
002f
002d
002a
0028
0026
0024
0022
0020
001e
001c
001a
0018
0017
0015
0013
0012
0010
000f
000d
000c
000b
0009
0008
0007
0006
0005
0004
0003
0002
0001
0000
ffff
ffff
fffe
fffd
fffd
fffc
fffb
fffb
fffa
fffa
fff9
fff9
fff9
fff8
fff8
fff8
fff8
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff7
fff8
fff8
fff8
fff8
fff8
fff8
fff9
fff9
fff9
fff9
fff9
fff9
fffa
fffa
fffa
fffa
fffa
fffb
fffb
fffb
fffb
fffb
fffb
fffc
fffc
fffc
fffc
fffc
fffc
fffd
fffd
fffd
fffd
fffd
fffd
fffd
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
fffe
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff

View File

@ -0,0 +1,449 @@
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
fffe
fffe
fffe
fffe
fffe
fffe
fffd
fffd
fffd
fffd
fffd
fffc
fffc
fffc
fffc
fffb
fffb
fffb
fffb
fffa
fffa
fffa
fff9
fff9
fff8
fff8
fff8
fff7
fff7
fff6
fff6
fff6
fff5
fff5
fff4
fff4
fff3
fff3
fff2
fff1
fff1
fff0
fff0
ffef
ffef
ffee
ffed
ffed
ffec
ffec
ffeb
ffeb
ffea
ffe9
ffe9
ffe8
ffe8
ffe7
ffe7
ffe6
ffe6
ffe5
ffe5
ffe4
ffe4
ffe4
ffe3
ffe3
ffe3
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe3
ffe3
ffe4
ffe4
ffe5
ffe5
ffe6
ffe7
ffe8
ffe9
ffea
ffeb
ffed
ffee
fff0
fff1
fff3
fff5
fff7
fff9
fffc
fffe
0001
0003
0006
0009
000c
000f
0013
0016
001a
001e
0022
0026
002b
002f
0034
0039
003e
0043
0048
004e
0054
0059
005f
0066
006c
0072
0079
0080
0087
008e
0095
009d
00a4
00ac
00b4
00bc
00c4
00cd
00d5
00de
00e6
00ef
00f8
0101
010a
0113
011d
0126
0130
0139
0143
014d
0156
0160
016a
0174
017e
0188
0192
019c
01a6
01b0
01ba
01c3
01cd
01d7
01e1
01eb
01f4
01fe
0207
0211
021a
0223
022c
0235
023e
0247
024f
0258
0260
0268
0270
0277
027f
0286
028d
0294
029a
02a1
02a7
02ad
02b3
02b8
02bd
02c2
02c7
02cb
02cf
02d3
02d6
02d9
02dc
02df
02e1
02e3
02e5
02e6
02e7
02e8
02e8
0698
02e8
02e8
02e7
02e6
02e5
02e3
02e1
02df
02dc
02d9
02d6
02d3
02cf
02cb
02c7
02c2
02bd
02b8
02b3
02ad
02a7
02a1
029a
0294
028d
0286
027f
0277
0270
0268
0260
0258
024f
0247
023e
0235
022c
0223
021a
0211
0207
01fe
01f4
01eb
01e1
01d7
01cd
01c3
01ba
01b0
01a6
019c
0192
0188
017e
0174
016a
0160
0156
014d
0143
0139
0130
0126
011d
0113
010a
0101
00f8
00ef
00e6
00de
00d5
00cd
00c4
00bc
00b4
00ac
00a4
009d
0095
008e
0087
0080
0079
0072
006c
0066
005f
0059
0054
004e
0048
0043
003e
0039
0034
002f
002b
0026
0022
001e
001a
0016
0013
000f
000c
0009
0006
0003
0001
fffe
fffc
fff9
fff7
fff5
fff3
fff1
fff0
ffee
ffed
ffeb
ffea
ffe9
ffe8
ffe7
ffe6
ffe5
ffe5
ffe4
ffe4
ffe3
ffe3
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe2
ffe3
ffe3
ffe3
ffe4
ffe4
ffe4
ffe5
ffe5
ffe6
ffe6
ffe7
ffe7
ffe8
ffe8
ffe9
ffe9
ffea
ffeb
ffeb
ffec
ffec
ffed
ffed
ffee
ffef
ffef
fff0
fff0
fff1
fff1
fff2
fff3
fff3
fff4
fff4
fff5
fff5
fff6
fff6
fff6
fff7
fff7
fff8
fff8
fff8
fff9
fff9
fffa
fffa
fffa
fffb
fffb
fffb
fffb
fffc
fffc
fffc
fffc
fffd
fffd
fffd
fffd
fffd
fffe
fffe
fffe
fffe
fffe
fffe
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff

View File

@ -0,0 +1,465 @@
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff
fffe
fffe
fffe
fffe
fffe
fffd
fffd
fffd
fffc
fffc
fffc
fffb
fffb
fffa
fffa
fffa
fff9
fff9
fff8
fff7
fff7
fff6
fff5
fff5
fff4
fff3
fff2
fff2
fff1
fff0
ffef
ffee
ffed
ffec
ffeb
ffe9
ffe8
ffe7
ffe6
ffe4
ffe3
ffe2
ffe0
ffdf
ffdd
ffdc
ffda
ffd9
ffd7
ffd5
ffd4
ffd2
ffd0
ffce
ffcd
ffcb
ffc9
ffc7
ffc5
ffc3
ffc2
ffc0
ffbe
ffbc
ffba
ffb8
ffb6
ffb5
ffb3
ffb1
ffb0
ffae
ffac
ffab
ffaa
ffa8
ffa7
ffa6
ffa5
ffa4
ffa3
ffa2
ffa2
ffa1
ffa1
ffa1
ffa1
ffa1
ffa1
ffa2
ffa2
ffa3
ffa4
ffa6
ffa7
ffa9
ffab
ffae
ffb1
ffb4
ffb7
ffba
ffbe
ffc3
ffc7
ffcc
ffd2
ffd7
ffdd
ffe4
ffeb
fff2
fffa
0002
000a
0013
001d
0027
0031
003c
0047
0053
005f
006c
007a
0087
0096
00a5
00b4
00c4
00d4
00e5
00f7
0109
011b
012e
0142
0156
016a
0180
0195
01ab
01c2
01d9
01f1
0209
0221
023a
0254
026e
0288
02a3
02be
02d9
02f5
0312
032e
034b
0368
0386
03a4
03c2
03e0
03ff
041d
043c
045b
047b
049a
04b9
04d9
04f8
0518
0537
0557
0576
0596
05b5
05d4
05f3
0612
0630
064f
066d
068a
06a8
06c5
06e2
06fe
071a
0735
0750
076b
0785
079e
07b7
07cf
07e7
07fe
0814
082a
083f
0853
0866
0879
088b
089c
08ac
08bb
08ca
08d8
08e4
08f0
08fb
0905
090f
0917
091e
0924
092a
092e
0932
0934
0935
114e
0935
0934
0932
092e
092a
0924
091e
0917
090f
0905
08fb
08f0
08e4
08d8
08ca
08bb
08ac
089c
088b
0879
0866
0853
083f
082a
0814
07fe
07e7
07cf
07b7
079e
0785
076b
0750
0735
071a
06fe
06e2
06c5
06a8
068a
066d
064f
0630
0612
05f3
05d4
05b5
0596
0576
0557
0537
0518
04f8
04d9
04b9
049a
047b
045b
043c
041d
03ff
03e0
03c2
03a4
0386
0368
034b
032e
0312
02f5
02d9
02be
02a3
0288
026e
0254
023a
0221
0209
01f1
01d9
01c2
01ab
0195
0180
016a
0156
0142
012e
011b
0109
00f7
00e5
00d4
00c4
00b4
00a5
0096
0087
007a
006c
005f
0053
0047
003c
0031
0027
001d
0013
000a
0002
fffa
fff2
ffeb
ffe4
ffdd
ffd7
ffd2
ffcc
ffc7
ffc3
ffbe
ffba
ffb7
ffb4
ffb1
ffae
ffab
ffa9
ffa7
ffa6
ffa4
ffa3
ffa2
ffa2
ffa1
ffa1
ffa1
ffa1
ffa1
ffa1
ffa2
ffa2
ffa3
ffa4
ffa5
ffa6
ffa7
ffa8
ffaa
ffab
ffac
ffae
ffb0
ffb1
ffb3
ffb5
ffb6
ffb8
ffba
ffbc
ffbe
ffc0
ffc2
ffc3
ffc5
ffc7
ffc9
ffcb
ffcd
ffce
ffd0
ffd2
ffd4
ffd5
ffd7
ffd9
ffda
ffdc
ffdd
ffdf
ffe0
ffe2
ffe3
ffe4
ffe6
ffe7
ffe8
ffe9
ffeb
ffec
ffed
ffee
ffef
fff0
fff1
fff2
fff2
fff3
fff4
fff5
fff5
fff6
fff7
fff7
fff8
fff9
fff9
fffa
fffa
fffa
fffb
fffb
fffc
fffc
fffc
fffd
fffd
fffd
fffe
fffe
fffe
fffe
fffe
ffff
ffff
ffff
ffff
ffff
ffff
ffff
ffff

123
source/fir_filter.sv Normal file
View File

@ -0,0 +1,123 @@
`timescale 1ns/100ps
`default_nettype none
module fir_filter #(parameter LEN = 449,
parameter COEFFS_ROM_FILE = "fir_449_50hz_100hz_10db_40db.rom")
(input wire clock,
input wire reset,
input wire signed [15:0] data_i,
input wire ready_i,
output wire signed [15:0] data_o,
output reg valid_o);
localparam LEN_CW = $clog2(LEN);
localparam MEM_LEN = 1 << LEN_CW;
/* Coeffs */
logic signed [15:0] coeffs[LEN];
initial $readmemh(COEFFS_ROM_FILE, coeffs, 0, LEN-1);
logic signed [15:0] coeff;
logic [LEN_CW-1:0] coeff_addr;
always_ff @ (posedge clock)
coeff <= coeffs[coeff_addr];
/* Z-1 BlockRAM */
logic signed [15:0] mem[MEM_LEN];
logic signed [15:0] mem_wdata;
logic signed [15:0] mem_rdata;
logic [LEN_CW-1:0] mem_addr;
logic mem_wr;
always_ff @ (posedge clock) begin
if (mem_wr)
mem[mem_addr] <= mem_wdata;
mem_rdata <= mem[mem_addr];
end
initial begin
integer i;
for (i = 0; i < MEM_LEN; i++)
mem[i] = '0;
end
/* MAC */
logic [31:0] mac_o;
logic signed [15:0] a;
logic signed [15:0] b;
logic signed [15:0] s;
ice40_mac16x16 #(.SIGNED(1)) mac
(.clock, .reset,
.a(a),
.b(b),
.s({s, 16'b0}),
.sub(1'b0),
.y(mac_o));
/* FSM */
enum int unsigned {
ST_IDLE = 0,
ST_WRITE,
ST_CONV,
ST_DONE
} state;
logic [LEN_CW-1:0] new_addr;
assign mem_wdata = data_i;
assign data_o = s;
assign s = coeff_addr == '0 ? '0 : mac_o[31:16];
always_ff @ (posedge clock, posedge reset)
if (reset) begin
state <= ST_IDLE;
new_addr <= '0;
mem_wr <= 1'b0;
valid_o <= 1'b0;
end
else
case (state)
ST_IDLE: begin
a <= '0;
b <= '0;
mem_addr <= new_addr;
coeff_addr <= '0;
if (ready_i) begin
mem_wr <= 1'b1;
state <= ST_WRITE;
end
end
ST_WRITE: begin
mem_wr <= 1'b0;
state <= ST_CONV;
end
ST_CONV: begin
a <= mem_rdata;
b <= coeff;
if (coeff_addr == LEN_CW'(LEN-1)) begin
valid_o <= 1'b1;
state <= ST_DONE;
end
else begin
coeff_addr <= coeff_addr + 1'b1;
mem_addr <= mem_addr - 1'b1;
state <= ST_CONV;
end
end
ST_DONE: begin
new_addr <= new_addr + 1'b1;
valid_o <= 1'b0;
state <= ST_IDLE;
end
endcase
endmodule // fir_filter

239
source/hsl2rgb.sv Normal file
View File

@ -0,0 +1,239 @@
`timescale 1ns/100ps
`default_nettype none
/**
* HSL to RGB translation.
*
* H, S, L = [0..1)
*
* Q = | L < 0.5 ? L + L*S
* | L >= 0.5 ? L + S - L*S
*
* P = 2 * L - Q
*
* TR = H < 2/3 ? H + 1/3 : 1/3 - (1 - H)
* TG = H
* TB = H >= 1/3 ? H - 1/3 : 1 - H
*
* COLORX = | TX < 1/6 ? P + ((Q - P) * 6 * TX)
* | 1/6 <= TX < 1/2 ? Q
* | 1/2 <= TX < 2/3 ? P + ((Q - P) * (2/3 - TX) * 6)
* | else : P
*/
/*
* Datapath:
*
* if l < [1/2]
* then: lls = l + (l * s)
* else: lls = l - (l * s)
*
* m1h = ~(h - 1)
*
* tr =
* if h < [2/3]
* then: h + [1/3]
* else: [1/3] - m1h
*
* tg = h
*
* tb =
* if h >= [1/3]
* then: h - [1/3]
* else: m1h
*
* q =
* if l < [1/2]
* then: lls
* else: lls + s
*
* p = l * 2 - q
* qp = (q - l) * 2
*
* r =
* p + 6 * qp * tr
* p + 6 * qp * ([2/3] - tr)
*
* g =
* p + 6 * qp * tg
* p + 6 * qp * ([2/3] - tg)
*
* b =
* p + 6 * qp * tb
* p + 6 * qp * ([2/3] - tb)
*/
module hsl2rgb
(input wire clock,
input wire reset,
input wire [7:0] h,
input wire [7:0] s,
input wire [7:0] l,
input wire ready_i,
output reg [7:0] r,
output reg [7:0] g,
output reg [7:0] b,
output wire valid_o);
`define C1_6 43
`define C1_3 85
`define C1_2 128
`define C2_3 171
`define C1_1 256
localparam STAGES = 5;
logic [STAGES-1:0] valid;
assign valid_o = valid[STAGES-1];
always_ff @ (posedge clock, posedge reset)
if (reset)
valid <= '0;
else
valid <= { valid[STAGES-2:0], ready_i };
/* ---------------- Stage 1 ---------------- */
logic [8:0] lls; // lls = l0 ± (l0 * s0)
logic ls_sub;
/* verilator lint_off UNUSED */
logic [31:0] mac_lls_o;
/* verilator lint_on UNUSED */
assign lls = {mac_lls_o[16:8]};
assign ls_sub = l < `C1_2 ? 1'b0 : 1'b1;
ice40_mac16x16 mac_lls
(.clock, .reset,
.a({8'b0, l}),
.b({8'b0, s}),
.s({16'b0, l, 8'b0}),
.sub(ls_sub),
.y(mac_lls_o));
/* propagate to next stage */
logic [7:0] h1, s1, l1;
always_ff @ (posedge clock) begin
h1 <= h;
s1 <= s;
l1 <= l;
end
/* ---------------- stage 2 ---------------- */
logic [8:0] q_pre;
logic [7:0] q;
logic [7:0] minus1h; // minus1h = 256 - h = ~(h - 1)
always_ff @ (posedge clock) begin
q_pre <= l1 < `C1_2 ? lls : lls + s1;
minus1h <= ~(h1 - 1);
end
assign q = q_pre[8] ? 8'hff : q_pre[7:0];
/* propagate to next stage */
logic [7:0] h2, l2;
always_ff @ (posedge clock) begin
h2 <= h1;
l2 <= l1;
end
/* ---------------- stage 3 ---------------- */
logic [7:0] tr, tg, tb;
logic [8:0] p_pre;
logic [7:0] p; // p = l * 2 - q
logic [7:0] qp; // qp = q - p
always_ff @ (posedge clock) begin
tr <= h2 < 8'(`C2_3) ? h2 + 8'(`C1_3) : 8'(`C1_3) - minus1h;
tg <= h2;
tb <= h2 >= 8'(`C1_3) ? h2 - 8'(`C1_3) : minus1h;
p_pre <= (9'(l2) << 1) - q;
qp <= 8'((q - l2) << 1);
end
assign p = p_pre[8] ? 8'hff : p_pre[7:0];
/* propagate to next stage */
logic [7:0] q3;
always_ff @ (posedge clock)
q3 <= q;
/* ---------------- stage 4 ---------------- */
logic [7:0] trx, tgx, tbx;
logic [10:0] qp6;
/* verilator lint_off UNUSED */
logic [31:0] mac_r_o;
logic [31:0] mac_g_o;
logic [31:0] mac_b_o;
/* verilator lint_on UNUSED */
assign qp6 = (11'(qp) << 1) + (11'(qp) << 2);
assign trx = (tr < `C1_6) ? tr : `C2_3 - tr;
assign tgx = (tg < `C1_6) ? tg : `C2_3 - tg;
assign tbx = (tb < `C1_6) ? tb : `C2_3 - tb;
ice40_mac16x16 mac_r
(.clock, .reset,
.a({5'b0, qp6}),
.b({8'b0, trx}),
.s({16'b0, p, 8'b0}),
.sub(1'b0),
.y(mac_r_o));
ice40_mac16x16 mac_g
(.clock, .reset,
.a({5'b0, qp6}),
.b({8'b0, tgx}),
.s({16'b0, p, 8'b0}),
.sub(1'b0),
.y(mac_g_o));
ice40_mac16x16 mac_b
(.clock, .reset,
.a({5'b0, qp6}),
.b({8'b0, tbx}),
.s({16'b0, p, 8'b0}),
.sub(1'b0),
.y(mac_b_o));
/* propagate to next stage */
logic [7:0] tr4, tg4, tb4;
logic [7:0] p4;
logic [7:0] q4;
always_ff @ (posedge clock) begin
tr4 <= tr;
tg4 <= tg;
tb4 <= tb;
p4 <= p;
q4 <= q3;
end
/* ---------------- stage 5 ---------------- */
always_ff @ (posedge clock) begin
if (tr4 < `C1_6) r <= mac_r_o[16] ? 8'hff : mac_r_o[15:8];
else if (tr4 < `C1_2) r <= q4;
else if (tr4 < `C2_3) r <= mac_r_o[16] ? 8'hff : mac_r_o[15:8];
else r <= p4;
end
always_ff @ (posedge clock) begin
if (tg4 < `C1_6) g <= mac_g_o[16] ? 8'hff : mac_g_o[15:8];
else if (tg4 < `C1_2) g <= q4;
else if (tg4 < `C2_3) g <= mac_g_o[16] ? 8'hff : mac_g_o[15:8];
else g <= p4;
end
always_ff @ (posedge clock) begin
if (tb4 < `C1_6) b <= mac_b_o[16] ? 8'hff : mac_b_o[15:8];
else if (tb4 < `C1_2) b <= q4;
else if (tb4 < `C2_3) b <= mac_b_o[16] ? 8'hff : mac_b_o[15:8];
else b <= p4;
end
endmodule // hsl2rgb

98
source/hsv2rgb.sv Normal file
View File

@ -0,0 +1,98 @@
`timescale 1ns/100ps
`default_nettype none
module hsv2rgb
(input wire clock,
input wire reset,
input wire [7:0] h,
input wire [7:0] s,
input wire [7:0] v,
input wire ready_i,
output reg [7:0] r,
output reg [7:0] g,
output reg [7:0] b,
output wire valid_o);
localparam STAGES = 2;
logic [STAGES-1:0] valid;
assign valid_o = valid[STAGES-1];
always_ff @ (posedge clock, posedge reset)
if (reset) valid <= '0;
else valid <= { valid[STAGES-2:0], ready_i };
/* ---------------- Stage 1 ---------------- */
logic [7:0] flip_s;
logic [7:0] vmin;
logic [31:0] mac_vmin_o;
logic [5:0] h_mod_43;
assign flip_s = 8'd255 - s;
assign vmin = mac_vmin_o[15:8];
always_ff @ (posedge clock)
h_mod_43
<= (h < 43) ? 6'(h) :
(h < 86) ? 6'(h - 8'd43) :
(h < 128) ? 6'(h - 8'd86) :
(h < 171) ? 6'(h - 8'd128) :
(h < 214) ? 6'(h - 8'd171) :
6'(h - 8'd214);
ice40_mac16x16 mac_lls
(.clock, .reset,
.a({8'b0, flip_s}),
.b({8'b0, v}),
.s(32'b0),
.sub(1'b0),
.y(mac_vmin_o));
logic [7:0] h1, v1;
always_ff @ (posedge clock) begin
h1 <= h;
v1 <= v;
end
/* ---------------- Stage 2 ---------------- */
logic [31:0] mac_a_o;
logic [7:0] h_mod_43_6;
logic [7:0] v_vmin;
logic [7:0] a;
assign a = mac_a_o[15:8];
assign h_mod_43_6 = (8'(h_mod_43) << 1) + (8'(h_mod_43) << 2);
assign v_vmin = v1 - vmin;
ice40_mac16x16 mac_a
(.clock, .reset,
.a({8'b0, v_vmin}),
.b({8'b0, h_mod_43_6}),
.s(32'b0),
.sub(1'b0),
.y(mac_a_o));
logic [7:0] h2, v2, vmin2;
always_ff @ (posedge clock) begin
h2 <= h1;
v2 <= v1;
vmin2 <= vmin;
end
/* ---------------- Output ---------------- */
logic [7:0] vinc, vdec;
assign vinc = vmin2 + a;
assign vdec = v2 - a;
always_comb
if (h2 < 43) {r, g, b} = {v2, vinc, vmin2};
else if (h2 < 86) {r, g, b} = {vdec, v2, vmin2};
else if (h2 < 128) {r, g, b} = {vmin2, v2, vinc};
else if (h2 < 171) {r, g, b} = {vmin2, vdec, v2};
else if (h2 < 214) {r, g, b} = {vinc, vmin2, v2};
else {r, g, b} = {v2, vmin2, vdec};
endmodule // hsv2rgb

135
source/hsx2rgb.rkt Normal file
View File

@ -0,0 +1,135 @@
#lang racket
(define (hsl2rgb h s l)
(define (color q p h)
(let ((tc (cond
((> h 1) (- h 1))
((< h 0) (+ h 1))
(else h))))
(cond
((< tc 1/6) (+ p (* (- q p) tc 6)))
((< tc 1/2) q)
((< tc 2/3) (+ p (* (- q p) (- 2/3 tc) 6)))
(else p))))
(let* ((q (if (< l 0.5)
(* l (+ 1 s))
(+ l s (- (* l s)))))
(p (- (* 2 l) q)))
(values
(inexact->exact (round (* 256 (color q p (+ h 1/3)))))
(inexact->exact (round (* 256 (color q p h))))
(inexact->exact (round (* 256 (color q p (- h 1/3))))))))
(define (hsl2rgb-ref h s l)
(hsl2rgb (/ h 256) (/ s 256) (/ l 256)))
(define (hsv2rgb h s v)
(let* ((hi (modulo (floor (/ h 256/6)) 6))
(vmin (/ (* v (- 256 s)) 256))
(a (* (- v vmin) (/ (modulo h 43) 256/6)))
(vinc (+ vmin a))
(vdec (- v a))
(vmin (inexact->exact (round vmin)))
(vinc (inexact->exact (round vinc)))
(vdec (inexact->exact (round vdec))))
(cond
((= hi 0) (values v vinc vmin))
((= hi 1) (values vdec v vmin))
((= hi 2) (values vmin v vinc))
((= hi 3) (values vmin vdec v))
((= hi 4) (values vinc vmin v))
((= hi 5) (values v vmin vdec)))))
(define (hsv2rgb-int h s v)
(define (byte-div-43-mod-6 x)
(cond
((< x 43) 0)
((< x 86) 1)
((< x 128) 2)
((< x 171) 3)
((< x 214) 4)
(else 5)))
(define (byte-mod-43 x)
(cond
((< x 43) x)
((< x 86) (- x 43))
((< x 128) (- x 86))
((< x 171) (- x 128))
((< x 214) (- x 171))
(else (- x 214))))
(define (*6 a) (+ (arithmetic-shift a 1)
(arithmetic-shift a 2)))
(let* ((hi (byte-div-43-mod-6 h))
(vmin (arithmetic-shift
(* (- 255 s) v) -8))
(a (arithmetic-shift
(* (- v vmin)
(*6 (byte-mod-43 h))) -8))
(vinc (+ vmin a))
(vdec (- v a)))
(printf "hi=~a, vmin=~a, a=~a, vinc=~a, vdec=~a\n" hi vmin a vinc vdec)
(cond
((= hi 0) (values v vinc vmin))
((= hi 1) (values vdec v vmin))
((= hi 2) (values vmin v vinc))
((= hi 3) (values vmin vdec v))
((= hi 4) (values vinc vmin v))
((= hi 5) (values v vmin vdec)))))
(define (hsl2rgb-int h s l)
(define (*fp8 a b) (arithmetic-shift (* a b) -8))
(define (*2 a) (arithmetic-shift a 1))
(define (*6 a) (+ (arithmetic-shift a 1)
(arithmetic-shift a 2)))
(define c1/6 43)
(define c1/3 85)
(define c1/2 128)
(define c2/3 171)
(define c1 256)
(let* ((l*s (*fp8 l s))
(q (if (< l c1/2)
(+ l l*s)
(+ l s (- l*s))))
(p (- (*2 l) q))
(q-p ;;(- q p)
(*2 (- q l)))
(tr (if (< h c2/3) [+ h c1/3] [- c1/3 (- c1 h)]))
(tg h)
(tb (if (>= h c1/3) [- h c1/3] [- c1 h]))
(q-p*6 (*6 q-p))
(r (cond
((< tr c1/6) [+ p (*fp8 q-p*6 tr)])
((< tr c1/2) q)
((< tr c2/3) [+ p (*fp8 q-p*6 (- c2/3 tr))])
(else p)))
(g (cond
((< tg c1/6) [+ p (*fp8 q-p*6 tg)])
((< tg c1/2) q)
((< tg c2/3) [+ p (*fp8 q-p*6 (- c2/3 tg))])
(else p)))
(b (cond
((< tb c1/6) [+ p (*fp8 q-p*6 tb)])
((< tb c1/2) q)
((< tb c2/3) [+ p (*fp8 q-p*6 (- c2/3 tb))])
(else p))))
;; (printf "q=~a p=~a q-p=~a tr=~a tg=~a tb=~a\n" q p q-p tr tg tb)
(values r g b)))
(define (p f h s x)
(let-values (((r g b) (f h s x)))
(printf "HSX ~a ~a ~a -> " h s x)
(printf "RGB ~a ~a ~a\n\n" r g b)))
;;(p 20 255 100)
(p hsv2rgb-int 50 100 150)
(p hsv2rgb-int 111 222 33)
(p hsv2rgb-int 200 150 50)

81
source/ice40_2mac8x8.sv Normal file
View File

@ -0,0 +1,81 @@
`timescale 1ns/100ps
`default_nettype none
/* verilator lint_off PINCONNECTEMPTY */
module ice40_2mac8x8 #(parameter SIGNED = 0)
(input wire clock,
input wire reset,
input wire [7:0] a0,
input wire [7:0] b0,
input wire [15:0] s0,
input wire sub0,
output wire [15:0] y0,
input wire [7:0] a1,
input wire [7:0] b1,
input wire [15:0] s1,
input wire sub1,
output wire [15:0] y1);
/* register 'sub' input */
logic sub0_r, sub1_r;
always_ff @ (posedge clock) begin
sub0_r <= sub0;
sub1_r <= sub1;
end
logic [31:0] mac_o;
assign {y0, y1} = mac_o;
SB_MAC16
#(.NEG_TRIGGER(1'b0),
.C_REG(1'b1), // Registered C
.A_REG(1'b1), // Registered A
.B_REG(1'b1), // Registered B
.D_REG(1'b1), // Registered D
.TOP_8x8_MULT_REG(1'b0),
.BOT_8x8_MULT_REG(1'b0),
.PIPELINE_16x16_MULT_REG1(1'b0),
.PIPELINE_16x16_MULT_REG2(1'b0),
.TOPOUTPUT_SELECT(2'b00), // TOP output - ADD/SUB unregistered
.TOPADDSUB_LOWERINPUT(2'b01), // TOP adder input 1 - 8x8 top multiplier output
.TOPADDSUB_UPPERINPUT(1'b1), // TOP adder input 2 - input C
.TOPADDSUB_CARRYSELECT(2'b00), // TOP adder carry input - constant 0
.BOTOUTPUT_SELECT(2'b00), // BOT output - ADD/SUB unregistered
.BOTADDSUB_LOWERINPUT(2'b01), // BOT adder input 1 - 8x8 bot multiplier output
.BOTADDSUB_UPPERINPUT(1'b1), // BOT adder input 2 - input D
.BOTADDSUB_CARRYSELECT(2'b00), // BOT adder carry input - constant 0
.MODE_8x8(1'b1),
.A_SIGNED(SIGNED),
.B_SIGNED(SIGNED))
mac_r
(.CLK(clock),
.CE(1'b1),
.C(s0),
.A({a0, a1}),
.B({b0, b1}),
.D(s1),
.AHOLD(1'b0),
.BHOLD(1'b0),
.CHOLD(1'b0),
.DHOLD(1'b0),
.IRSTTOP(reset),
.IRSTBOT(reset),
.ORSTTOP(reset),
.ORSTBOT(reset),
.OLOADTOP(1'b0),
.OLOADBOT(1'b0),
.ADDSUBTOP(sub0_r),
.ADDSUBBOT(sub1_r),
.OHOLDTOP(1'b0),
.OHOLDBOT(1'b0),
.CI(1'b0),
.ACCUMCI(1'b0),
.SIGNEXTIN(1'b0),
.O(mac_o),
.CO(),
.ACCUMCO(),
.SIGNEXTOUT());
endmodule // ice40_macadd16x16

70
source/ice40_mac16x16.sv Normal file
View File

@ -0,0 +1,70 @@
`timescale 1ns/100ps
`default_nettype none
/* verilator lint_off PINCONNECTEMPTY */
module ice40_mac16x16 #(parameter SIGNED = 0)
(input wire clock,
input wire reset,
input wire [15:0] a,
input wire [15:0] b,
input wire [31:0] s,
input wire sub,
output wire [31:0] y);
/* register 'sub' input */
logic sub_r;
always_ff @ (posedge clock)
sub_r <= sub;
SB_MAC16
#(.NEG_TRIGGER(1'b0),
.C_REG(1'b1), // Registered C
.A_REG(1'b1), // Registered A
.B_REG(1'b1), // Registered B
.D_REG(1'b1), // Registered D
.TOP_8x8_MULT_REG(1'b0),
.BOT_8x8_MULT_REG(1'b0),
.PIPELINE_16x16_MULT_REG1(1'b0),
.PIPELINE_16x16_MULT_REG2(1'b0),
.TOPOUTPUT_SELECT(2'b00), // TOP output - ADD/SUB unregistered
.TOPADDSUB_LOWERINPUT(2'b10), // TOP adder input 1 - 16x16 multiplier upper word
.TOPADDSUB_UPPERINPUT(1'b1), // TOP adder input 2 - input C
.TOPADDSUB_CARRYSELECT(2'b00), // TOP adder carry input - constant 0
.BOTOUTPUT_SELECT(2'b00), // BOT output - ADD/SUB unregistered
.BOTADDSUB_LOWERINPUT(2'b10), // BOT adder input 1 - 16x16 multiplier lower word
.BOTADDSUB_UPPERINPUT(1'b1), // BOT adder input 2 - input D
.BOTADDSUB_CARRYSELECT(2'b00), // BOT adder carry input - constant 0
.MODE_8x8(1'b0),
.A_SIGNED(SIGNED),
.B_SIGNED(SIGNED))
mac_r
(.CLK(clock),
.CE(1'b1),
.C(s[31:16]),
.A(a),
.B(b),
.D(s[15:0]),
.AHOLD(1'b0),
.BHOLD(1'b0),
.CHOLD(1'b0),
.DHOLD(1'b0),
.IRSTTOP(reset),
.IRSTBOT(reset),
.ORSTTOP(reset),
.ORSTBOT(reset),
.OLOADTOP(1'b0),
.OLOADBOT(1'b0),
.ADDSUBTOP(sub_r),
.ADDSUBBOT(sub_r),
.OHOLDTOP(1'b0),
.OHOLDBOT(1'b0),
.CI(1'b0),
.ACCUMCI(1'b0),
.SIGNEXTIN(1'b0),
.O(y),
.CO(),
.ACCUMCO(),
.SIGNEXTOUT());
endmodule // ice40_macadd16x16

94
source/ice40_spram.sv Normal file
View File

@ -0,0 +1,94 @@
`timescale 1ns/100ps
`default_nettype none
module ice40_spram
(input wire clock,
input wire [15:0] addr,
input wire [15:0] data_i,
output wire [15:0] data_o,
input wire wr);
logic [15:0] data0_o;
logic [15:0] data1_o;
logic [15:0] data2_o;
logic [15:0] data3_o;
logic w0, w1, w2, w3;
logic [15:0] datax_o;
assign data_o = datax_o;
always @(*) begin
{w0, w1, w2, w3} = '0;
case (addr[15:14])
2'd0: begin
datax_o = data0_o;
w0 = wr;
end
2'd1: begin
datax_o = data1_o;
w1 = wr;
end
2'd2: begin
datax_o = data2_o;
w2 = wr;
end
2'd3: begin
datax_o = data3_o;
w3 = wr;
end
endcase
end
SB_SPRAM256KA spram0
(.CLOCK(clock),
.ADDRESS(addr[13:0]),
.DATAIN(data_i),
.DATAOUT(data0_o),
.WREN(w0),
.MASKWREN({w0, w0, w0, w0}),
.CHIPSELECT(1'b1),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1));
SB_SPRAM256KA spram1
(.CLOCK(clock),
.ADDRESS(addr[13:0]),
.DATAIN(data_i),
.DATAOUT(data1_o),
.WREN(w1),
.MASKWREN({w1, w1, w1, w1}),
.CHIPSELECT(1'b1),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1));
SB_SPRAM256KA spram2
(.CLOCK(clock),
.ADDRESS(addr[13:0]),
.DATAIN(data_i),
.DATAOUT(data2_o),
.WREN(w2),
.MASKWREN({w2, w2, w2, w2}),
.CHIPSELECT(1'b1),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1));
SB_SPRAM256KA spram3
(.CLOCK(clock),
.ADDRESS(addr[13:0]),
.DATAIN(data_i),
.DATAOUT(data3_o),
.WREN(w3),
.MASKWREN({w3, w3, w3, w3}),
.CHIPSELECT(1'b1),
.STANDBY(1'b0),
.SLEEP(1'b0),
.POWEROFF(1'b1));
endmodule // ice40_spram

80
source/lcd_init.rom Normal file
View File

@ -0,0 +1,80 @@
0CF // ---- Power control B
100 // default
1C9 // (or C1) непонятно что это. По документу вообще нужно 89 или 81
130 // default
0ED // ---- Power on sequence
164 // ?
103 // ?
112 // ?
181 // DDVDH enhance mode
0E8 // ---- Driver timing control A
185 // Gate driver overlap timing +1unit (?)
110 // ?
17A // Precharge (default)
0CB // ---- Power control A
139 // default
12C // default
100 // default
134 // default (Vcore = 1.6V)
102 // default (DDVDH = 5.6V)
0F7 // ---- Pump ratio control
120 // DDVDH = 2xVCI
0EA // ---- Driver timing control B
100 // ?
100 // default
0C0 // ---- Power control 1
11B // VRH = 4.2V
0C1 // ---- Power control 2
100 // ?
0C5 // ---- VCOM control 1
130 // (or 3F) VCOMH = 3.9V
130 // (or 3C) VCOML = -1.3V
0C7 // ---- VCOM control 2
1B7 // VCOMH = VMH-9, VCOML = VML-9
036 // ---- Memory Access Control
108 // RGB reverse
03A // ---- Pixel format set
155 // 66 - 18 bit per pixel, 55 - 16 bit per pixel
0F6 // ---- Interface control
101 // default
110 // EPF=01 (RGB mapping to 16-bit word)
0B1 // ---- Frame rate control
100 // fosc/1
11A // 26 clock per line (default - 1B)
0B6 // ---- Display Function Control (отсутствуют два параметра)
10A // default
1A2 // Reverse shift direction
0F2 // ---- 3Gamma Function Disable
100
026 // ---- Gamma curve
101 // default
02B // ---- Page address set 0..319
100
100
101
13f
02A // ---- Column address set 0..239
100
100
100
1ef
011 // ---- Exit Sleep

98
source/lcd_spi.sv Normal file
View File

@ -0,0 +1,98 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
module lcd_spi #(parameter DATA_WIDTH = 8,
parameter SPI_CLK_PERIOD = 10,
parameter PUSH_ON_DONE = 0)
(input wire clock,
input wire reset,
input wire [DATA_WIDTH-1:0] data_i,
input wire push_i,
output reg done_o,
output reg spi_clk_o,
output wire spi_dat_o);
initial begin
`assert(DATA_WIDTH > 0);
`assert(SPI_CLK_PERIOD >= 2);
end
localparam SCLK_LOW = SPI_CLK_PERIOD / 2;
logic [$clog2(SPI_CLK_PERIOD)-1:0] sclk_cntr;
logic sclk_one;
logic sclk_nededge;
logic do_push;
assign sclk_one = (sclk_cntr < SCLK_LOW) ? 1'b0 : 1'b1;
always_ff @ (posedge clock, posedge reset)
if (reset) begin
sclk_cntr <= '0;
sclk_nededge <= 1'b0;
spi_clk_o <= 1'b0;
end
else begin
sclk_nededge <= 1'b0;
spi_clk_o <= do_push ? sclk_one : 1'b0;
if (do_push || sclk_one) begin
if (sclk_cntr == (SPI_CLK_PERIOD-1)) begin
sclk_cntr <= '0;
sclk_nededge <= 1'b1;
end
else
sclk_cntr <= sclk_cntr + 1'b1;
end
else
sclk_cntr <= '0;
end
localparam BIT_CW = $clog2(DATA_WIDTH);
logic [BIT_CW-1:0] sbit_cntr;
logic [DATA_WIDTH-1:0] data_sr;
assign spi_dat_o = data_sr[DATA_WIDTH-1];
logic push;
generate
if (PUSH_ON_DONE == 0)
assign push = push_i & done_o;
else
assign push = push_i;
endgenerate
always_ff @(posedge clock, posedge reset)
if (reset) begin
do_push <= 1'b0;
done_o <= 1'b0;
end
else begin
if (do_push) begin
if (sclk_nededge) begin
data_sr <= data_sr << 1;
sbit_cntr <= sbit_cntr + 1'b1;
if (sbit_cntr == BIT_CW'(DATA_WIDTH-1)) begin
do_push <= 1'b0;
done_o <= 1'b1;
end
end
end
else begin
done_o <= 1'b0;
if (push) begin
data_sr <= data_i;
sbit_cntr <= '0;
do_push <= 1'b1;
end
end
end
endmodule // lcd_spi

737
source/lcd_top.sv Normal file
View File

@ -0,0 +1,737 @@
`timescale 1ns/100ps
`default_nettype none
/* Yosys do not support SPRAM and MAC inferring */
`define USE_SPRAM_PRIMITIVE
`define USE_MAC_PRIMITIVE
module lcd_top #(parameter SPI_CLK_PERIOD = 6)
(input logic clock,
input logic reset,
output logic lcd_spi_csn_o,
output logic lcd_spi_clk_o,
output logic lcd_spi_dat_o,
output logic lcd_spi_dcn_o,
input logic redraw_i,
output logic done_o,
input logic [7:0] x_i,
input logic [8:0] y_i,
input logic [15:0] color_i,
output logic [15:0] color_o,
input logic req_i,
output logic ack_o,
input logic wr_i);
/* Display size */
localparam DISPLAY_MEM_SIZE = 320 * 240 * 2; // 2 byte per pixel
localparam DISPLAY_MEM_CW = $clog2(DISPLAY_MEM_SIZE);
/* Display x/y address width */
localparam DISPLAY_XCW = 8,
DISPLAY_YCW = 9;
/* Screen size and origin */
localparam XSIZE = 240,
YSIZE = 272,
XORIG = 0,
YORIG = 24;
localparam FBSIZE = XSIZE * YSIZE;
localparam FBCW = $clog2(FBSIZE);
/* Drawing block size */
localparam BLOCK_XS = 16, // must be power of 2
BLOCK_YS = 16; // must be power of 2
localparam GRID_XS = XSIZE / BLOCK_XS,
GRID_YS = YSIZE / BLOCK_YS;
localparam BLOCK_XS_CW = $clog2(BLOCK_XS);
localparam BLOCK_YS_CW = $clog2(BLOCK_YS);
localparam GRID_XS_CW = $clog2(GRID_XS);
localparam GRID_YS_CW = $clog2(GRID_YS);
localparam DFLAG_SIZE = GRID_XS * GRID_YS;
localparam DFLAG_CW = $clog2(DFLAG_SIZE);
localparam BLOCK_SIZE = BLOCK_XS * BLOCK_YS;
localparam BLOCK_CW = $clog2(BLOCK_SIZE);
/* --------- Drawing flags memory --------- */
logic dflag[DFLAG_SIZE];
logic dflag_wdata;
logic dflag_rdata;
logic [DFLAG_CW-1:0] dflag_waddr;
logic [DFLAG_CW-1:0] dflag_raddr;
logic [DFLAG_CW-1:0] dflag_int_addr; // for screen refresher
logic [DFLAG_CW-1:0] dflag_ext_addr; // for external master
logic dflag_wr;
logic dflag_set;
logic dflag_clr;
assign dflag_wr = dflag_set | dflag_clr;
assign dflag_waddr = dflag_clr ? dflag_int_addr : dflag_ext_addr;
assign dflag_raddr = dflag_int_addr;
assign dflag_wdata = dflag_set;
/* Infer as sysMEM Block RAM */
always_ff @ (posedge clock) begin
if (dflag_wr)
dflag[dflag_waddr] <= dflag_wdata;
dflag_rdata <= dflag[dflag_raddr];
end
/* --------- Frame buffer RAM (one-port block RAM)--------- */
logic [FBCW-1:0] fbaddr;
logic [15:0] fb_rdata;
logic [15:0] fb_wdata;
logic fbwrite;
`ifdef USE_SPRAM_PRIMITIVE
ice40_spram spram_i
(.clock(clock),
.addr(fbaddr),
.data_i(fb_wdata),
.data_o(fb_rdata),
.wr(fbwrite));
`else
logic [15:0] fbram[FBSIZE];
always_ff @ (posedge clock)
if (fbwrite) begin
fbram[fbaddr] <= fb_wdata;
fb_rdata <= 'x;
end
else
fb_rdata <= fbram[fbaddr];
`endif
/* --------- Framebuffer arbiter --------- */
logic [FBCW-1:0] fba_int; // Frame buffer address from refresher
logic [FBCW-1:0] fba_ext; // Frame buffer address from client
logic fb_busy_int;
logic fb_busy_ext;
logic fb_clear;
assign fbaddr = fb_busy_int ? fba_int : fba_ext;
assign color_o = fb_rdata;
assign fb_wdata = fb_clear ? '0 : color_i;
enum int unsigned {
FBST_CLEAR = 0,
FBST_CLEAR_NEXT,
FBST_WAIT,
FBST_ADDR,
FBST_READ,
FBST_DONE
} fbst;
logic [FBCW-1:0] ymult;
logic [DFLAG_CW-1:0] fmult;
`ifdef USE_MAC_PRIMITIVE
wire [31:0] ymac_o;
wire [31:0] fmac_o;
ice40_mac16x16 ymac_i
(.clock, .reset,
.a(16'(y_i)),
.b(16'(XSIZE)),
.s(32'b0),
.sub(1'b0),
.y(ymac_o));
ice40_mac16x16 fmac_i
(.clock, .reset,
.a(16'(y_i) >> BLOCK_YS_CW),
.b(16'(GRID_XS)),
.s(32'b0),
.sub(1'b0),
.y(fmac_o));
assign ymult = ymac_o[FBCW-1:0];
assign fmult = fmac_o[DFLAG_CW-1:0];
`endif
always_ff @ (posedge clock, posedge reset)
if (reset) begin
fbst <= FBST_CLEAR;
ack_o <= 1'b0;
fbwrite <= 1'b0;
fb_busy_ext <= 1'b0;
dflag_set <= 1'b0;
end
else
case (fbst)
/* Clear frame buffer RAM */
FBST_CLEAR: begin
fba_ext <= '0;
fbwrite <= 1'b1;
fb_busy_ext <= 1'b1;
fb_clear <= 1'b1;
fbst <= FBST_CLEAR_NEXT;
end
FBST_CLEAR_NEXT:
if (fba_ext == (FBSIZE-1)) begin
fbwrite <= 1'b0;
fb_busy_ext <= 1'b0;
fb_clear <= 1'b0;
fbst <= FBST_WAIT;
end
else
fba_ext <= fba_ext + 1'b1;
/* Main loop */
FBST_WAIT:
if (req_i && !fb_busy_int)
if (x_i >= XSIZE || y_i >= YSIZE) begin
ack_o <= 1'b1;
fbst <= FBST_READ;
end
else begin
`ifndef USE_MAC_PRIMITIVE
ymult <= y_i * XSIZE;
fmult <= GRID_YS_CW'(y_i >> BLOCK_YS_CW) * GRID_XS;
`endif
fb_busy_ext <= 1'b1;
fbst <= FBST_ADDR;
end
FBST_ADDR:
if (fb_busy_int) begin
fbst <= FBST_WAIT;
fb_busy_ext <= 1'b0;
end
else begin
fba_ext <= ymult + FBCW'(x_i);
dflag_ext_addr <= fmult + DFLAG_CW'(x_i >> BLOCK_XS_CW);
if (wr_i) begin
ack_o <= 1'b1;
fbwrite <= 1'b1;
dflag_set <= 1'b1;
fbst <= FBST_DONE;
end
else
fbst <= FBST_READ;
end
FBST_READ: begin
ack_o <= 1'b1;
fbst <= FBST_DONE;
end
FBST_DONE: begin
fbwrite <= 1'b0;
dflag_set <= 1'b0;
ack_o <= 1'b0;
fb_busy_ext <= 1'b0;
fbst <= FBST_WAIT;
end
endcase
/* --------- Read initialization commands from file --------- */
localparam INIT_FILE = "lcd_init.rom";
localparam INIT_ROM_SIZE = 64;
localparam INIT_DATA_SIZE = 61;
localparam INIT_ROM_CW = $clog2(INIT_ROM_SIZE);
logic [8:0] init_rom [INIT_ROM_SIZE];
logic [INIT_ROM_CW-1:0] init_addr;
logic [8:0] init_data;
initial $readmemh(INIT_FILE, init_rom, 0, INIT_DATA_SIZE-1);
/* Block RAM as ROM */
always_ff @ (posedge clock)
init_data <= init_rom[init_addr];
/* --------- SPI master --------- */
logic [7:0] spi_data;
logic spi_push;
logic spi_done;
lcd_spi #(.DATA_WIDTH(8),
.SPI_CLK_PERIOD(SPI_CLK_PERIOD),
.PUSH_ON_DONE(1)) spim_i
(.clock, .reset,
.data_i(spi_data),
.push_i(spi_push),
.done_o(spi_done),
.spi_clk_o(lcd_spi_clk_o),
.spi_dat_o(lcd_spi_dat_o));
/* --------- Main FSM --------- */
`ifdef TESTBENCH
localparam INIT_DELAY = 250;
`else
// localparam INIT_DELAY = 7500000;
localparam INIT_DELAY = 250;
`endif
enum int unsigned {
ST_PREINIT_DELAY = 0, // 0
ST_INIT_PUSH_SPI,
ST_INIT_WAIT_SPI,
ST_INIT_WAIT_LAST,
ST_POSTINIT_DELAY,
ST_DISPLAY_ON,
ST_DISPLAY_ONW,
ST_SCRCLR_CMD,
ST_SCRCLR,
ST_SCRCLR_LAST,
ST_START_REDRAW, // 10
ST_READ_FLAG,
ST_CHECK_FLAG,
ST_CLEAR_FLAG,
ST_CADDR_CMD,
ST_CADDR_0,
ST_CADDR_1,
ST_CADDR_2,
ST_CADDR_3,
ST_PADDR_CMD,
ST_PADDR_0, // 20
ST_PADDR_1,
ST_PADDR_2,
ST_PADDR_3,
ST_WRITE_CMD,
ST_WRITE_CMDW,
ST_FB_READ,
ST_STORE_PIXEL,
ST_WRITE_PIXEL_H,
ST_WRITE_PIXEL_L,
ST_NEXT_PIXEL, // 30
ST_NEXT_BLOCK
} state, next;
always_ff @(posedge clock, posedge reset)
if (reset) state <= ST_PREINIT_DELAY;
else state <= next;
/* Pre/post init delay counter */
localparam INIT_DELAY_CW = $clog2(INIT_DELAY);
logic [INIT_DELAY_CW-1:0] delay_cntr;
logic delay_cntr_incr;
always_ff @(posedge clock)
delay_cntr <= (~reset && delay_cntr_incr) ?
delay_cntr + 1'b1 : '0;
/* Initialization ROM address */
logic init_addr_rst;
logic init_addr_incr;
always_ff @ (posedge clock)
if (reset || init_addr_rst)
init_addr <= '0;
else
if (init_addr_incr)
init_addr <= init_addr + 1'b1;
/* LCD chipselect */
logic spi_disable;
always_ff @ (posedge clock)
if (reset || spi_disable)
lcd_spi_csn_o <= 1'b1;
else if (spi_push)
lcd_spi_csn_o <= 1'b0;
/* LCD data/command */
logic spi_do_cmd;
logic spi_do_data;
always_ff @ (posedge clock)
// if (spi_do_cmd)
// lcd_spi_dcn_o <= 1'b0;
// else if (spi_do_data)
// lcd_spi_dcn_o <= 1'b1;
if (spi_do_cmd != spi_do_data)
lcd_spi_dcn_o <= spi_do_data;
/* Redraw block coordinates */
logic [GRID_XS_CW-1:0] bx;
logic [DISPLAY_XCW-1:0] borigx;
logic [DISPLAY_YCW-1:0] borigy;
logic [DISPLAY_XCW-1:0] borigx_e;
logic [DISPLAY_YCW-1:0] borigy_e;
assign borigx_e = borigx + BLOCK_XS - 1;
assign borigy_e = borigy + BLOCK_YS - 1;
logic [FBCW-1:0] fbr_orig;
logic reset_redraw;
logic next_block;
logic is_last_blk;
always_ff @ (posedge clock)
if (reset_redraw) begin
dflag_int_addr <= '0;
bx <= '0;
borigx <= XORIG;
borigy <= YORIG;
fbr_orig <= '0;
is_last_blk <= 1'b0;
end
else
if (next_block) begin
dflag_int_addr <= dflag_int_addr + 1'b1;
is_last_blk <= (dflag_int_addr == DFLAG_CW'(DFLAG_SIZE-1)) ? 1'b1 : 1'b0;
if (bx == GRID_XS_CW'(GRID_XS - 1))
begin
bx <= '0;
borigx <= XORIG;
borigy <= borigy + BLOCK_YS;
fbr_orig <= fbr_orig + BLOCK_XS + ((BLOCK_YS - 1) * XSIZE);
end
else
begin
bx <= bx + 1'b1;
borigx <= borigx + BLOCK_XS;
fbr_orig <= fbr_orig + BLOCK_XS;
end
end
/* Framebuffer handling */
logic [BLOCK_XS_CW-1:0] px;
logic [BLOCK_CW-1:0] pi;
logic [15:0] pixel;
logic start_write;
logic read_pixel;
logic next_pixel;
always_ff @ (posedge clock)
if (read_pixel)
pixel <= fb_rdata;
always_ff @ (posedge clock)
if (start_write) begin
fba_int <= fbr_orig;
px <= '0;
pi <= '0;
end
else
if (next_pixel) begin
pi <= pi + 1'b1;
if (px == BLOCK_XS_CW'(BLOCK_XS-1))
begin
fba_int <= fba_int + XSIZE - BLOCK_XS + 1'b1;
px <= '0;
end
else
begin
fba_int <= fba_int + 1'b1;
px <= px + 1'b1;
end
end
/* Column/page addresses map */
logic [15:0] caddr_b, caddr_e;
logic [15:0] paddr_b, paddr_e;
assign caddr_b = { (16-DISPLAY_XCW)'('0), borigx };
assign caddr_e = { (16-DISPLAY_XCW)'('0), borigx_e };
assign paddr_b = { (16-DISPLAY_YCW)'('0), borigy };
assign paddr_e = { (16-DISPLAY_YCW)'('0), borigy_e };
/* Clear screen pixel counter */
logic [DISPLAY_MEM_CW-1:0] scrclr_pcntr;
logic scrclr_incr;
always_ff @ (posedge clock)
if (reset || init_addr_rst)
scrclr_pcntr <= '0;
else
if (scrclr_incr)
scrclr_pcntr <= scrclr_pcntr + 1'b1;
/* FSM combinational block */
always @(*) begin
next = state;
/* FSM outputs default value */
delay_cntr_incr = 1'b0;
init_addr_rst = 1'b0;
init_addr_incr = 1'b0;
scrclr_incr = 1'b0;
spi_push = 1'b0;
spi_data = '0;
spi_disable = 1'b0;
spi_do_cmd = 1'b0;
spi_do_data = 1'b0;
reset_redraw = 1'b0;
dflag_clr = 1'b0;
next_block = 1'b0;
start_write = 1'b0;
next_pixel = 1'b0;
read_pixel = 1'b0;
fb_busy_int = 1'b0;
done_o = 1'b0;
case (state)
ST_PREINIT_DELAY: begin
delay_cntr_incr = 1'b1;
init_addr_rst = 1'b1;
if (delay_cntr == INIT_DELAY)
next = ST_INIT_PUSH_SPI;
end
ST_INIT_PUSH_SPI: begin
init_addr_incr = 1'b1;
spi_data = init_data[7:0];
spi_push = 1'b1;
{spi_do_cmd, spi_do_data}
= init_data[8] ? 2'b01 : 2'b10;
if (init_addr == (INIT_DATA_SIZE-1))
next = ST_INIT_WAIT_LAST;
else
next = ST_INIT_WAIT_SPI;
end
ST_INIT_WAIT_SPI:
if (spi_done)
next = ST_INIT_PUSH_SPI;
ST_INIT_WAIT_LAST:
if (spi_done)
next = ST_POSTINIT_DELAY;
ST_POSTINIT_DELAY: begin
spi_disable = 1'b1;
delay_cntr_incr = 1'b1;
if (delay_cntr == INIT_DELAY)
next = ST_DISPLAY_ON;
end
ST_DISPLAY_ON: begin
spi_data = 8'h29;
spi_do_cmd = 1'b1;
spi_push = 1'b1;
next = ST_DISPLAY_ONW;
end
ST_DISPLAY_ONW:
if (spi_done) begin
spi_disable = 1'b1;
next = ST_SCRCLR_CMD;
end
ST_SCRCLR_CMD: begin
spi_data = 8'h2c;
spi_do_cmd = 1'b1;
spi_push = 1'b1;
next = ST_SCRCLR;
end
ST_SCRCLR:
if (spi_done) begin
spi_data = 8'h00; //8'h32;
spi_do_data = 1'b1;
spi_push = 1'b1;
scrclr_incr = 1'b1;
if (scrclr_pcntr ==
`ifdef TESTBENCH
`ifdef VERILATOR
(DISPLAY_MEM_SIZE - 1)
`else
200
`endif
`else
(DISPLAY_MEM_SIZE - 1)
`endif
)
next = ST_SCRCLR_LAST;
end
ST_SCRCLR_LAST:
if (spi_done)
next = ST_START_REDRAW;
ST_START_REDRAW:
if (redraw_i) begin
spi_disable = 1'b1;
reset_redraw = 1'b1;
next = ST_READ_FLAG;
end
ST_READ_FLAG:
if (is_last_blk) begin
done_o = 1'b1;
next = ST_START_REDRAW;
end
else
next = ST_CHECK_FLAG;
ST_CHECK_FLAG:
if (dflag_rdata == 1'b1)
next = ST_CLEAR_FLAG;
else
begin
next_block = 1'b1;
next = ST_READ_FLAG;
end
ST_CLEAR_FLAG: begin
dflag_clr = 1'b1;
next = ST_CADDR_CMD;
end
ST_CADDR_CMD: begin
spi_data = 8'h2a;
spi_do_cmd = 1'b1;
spi_push = 1'b1;
next = ST_CADDR_0;
end
ST_CADDR_0:
if (spi_done) begin
spi_data = caddr_b[15:8];
spi_do_data = 1'b1;
spi_push = 1'b1;
next = ST_CADDR_1;
end
ST_CADDR_1:
if (spi_done) begin
spi_data = caddr_b[7:0];
spi_push = 1'b1;
next = ST_CADDR_2;
end
ST_CADDR_2:
if (spi_done) begin
spi_data = caddr_e[15:8];
spi_push = 1'b1;
next = ST_CADDR_3;
end
ST_CADDR_3:
if (spi_done) begin
spi_data = caddr_e[7:0];
spi_push = 1'b1;
next = ST_PADDR_CMD;
end
ST_PADDR_CMD:
if (spi_done) begin
spi_data = 8'h2b;
spi_do_cmd = 1'b1;
spi_push = 1'b1;
next = ST_PADDR_0;
end
ST_PADDR_0:
if (spi_done) begin
spi_data = paddr_b[15:8];
spi_do_data = 1'b1;
spi_push = 1'b1;
next = ST_PADDR_1;
end
ST_PADDR_1:
if (spi_done) begin
spi_data = paddr_b[7:0];
spi_push = 1'b1;
next = ST_PADDR_2;
end
ST_PADDR_2:
if (spi_done) begin
spi_data = paddr_e[15:8];
spi_push = 1'b1;
next = ST_PADDR_3;
end
ST_PADDR_3:
if (spi_done) begin
spi_data = paddr_e[7:0];
spi_push = 1'b1;
next = ST_WRITE_CMD;
end
ST_WRITE_CMD:
if (spi_done) begin
spi_data = 8'h2c;
spi_do_cmd = 1'b1;
spi_push = 1'b1;
start_write = 1'b1;
next = ST_WRITE_CMDW;
end
ST_WRITE_CMDW:
if (spi_done)
next = ST_FB_READ;
ST_FB_READ:
if (!fb_busy_ext) begin
fb_busy_int = 1'b1;
next = ST_STORE_PIXEL;
end
ST_STORE_PIXEL: begin
fb_busy_int = 1'b1;
read_pixel = 1'b1;
next = ST_WRITE_PIXEL_H;
end
ST_WRITE_PIXEL_H: begin
spi_data = pixel[15:8];
spi_do_data = 1'b1;
spi_push = 1'b1;
next = ST_WRITE_PIXEL_L;
end
ST_WRITE_PIXEL_L:
if (spi_done) begin
spi_data = pixel[7:0];
spi_push = 1'b1;
next = ST_NEXT_PIXEL;
end
ST_NEXT_PIXEL:
if (spi_done) begin
next_pixel = 1'b1;
if (pi == (BLOCK_SIZE - 1))
next = ST_NEXT_BLOCK;
else
next = ST_FB_READ;
end
ST_NEXT_BLOCK: begin
spi_disable = 1'b1;
next_block = 1'b1;
next = ST_READ_FLAG;
end
endcase
end
endmodule // lcd_top

37
source/lfsr.sv Normal file
View File

@ -0,0 +1,37 @@
`timescale 1ns/100ps
`default_nettype none
module lfsr #(parameter POLY = 32'hA3000000)
(clock,
preset,
data_i,
prnd_o);
localparam WIDTH = $size(POLY);
input wire clock;
input wire preset;
input wire [WIDTH-1:0] data_i;
output wire prnd_o;
logic [WIDTH-1:0] sreg;
logic feedback;
initial sreg = '1;
assign feedback = sreg[0];
assign prnd_o = feedback;
integer i;
always_ff @ (posedge clock)
if (preset)
sreg <= (data_i == '0) ? '1 : data_i;
else begin
sreg[WIDTH-1] <= feedback;
for (i = 0; i < (WIDTH-1); i ++)
sreg[i] <= POLY[i] ? (sreg[i+1] ^ feedback) : sreg[i+1];
end
endmodule // lfsr

133
source/mcp3201_ma.sv Normal file
View File

@ -0,0 +1,133 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
/*
* MCP3201 controller
* Multichannel and sample rate accurate version.
*/
module mcp3201_ma #(parameter CHANNELS = 1,
parameter CLOCK_FREQ = 12000000,
parameter SCLK_FREQ = 1000000,
parameter SAMPLE_RATE = 44100)
(input wire clock,
input wire reset,
output reg spi_clk_o,
output reg spi_ssn_o,
input wire [CHANNELS-1:0] spi_miso_i,
output reg [CHANNELS*12-1:0] data_o,
output reg strb_o);
initial begin
`assert(CHANNELS > 0);
end
/* SCLK frequency not need accuracy */
localparam SCLK_PERIOD = CLOCK_FREQ/SCLK_FREQ;
localparam SCLK_CW = $clog2(SCLK_PERIOD);
logic [SCLK_CW-1:0] sclk_cnt;
logic sclk_posedge;
/* Make SPI SCLK */
always_ff @(posedge clock)
if (reset | spi_ssn_o) begin
spi_clk_o <= 1'b1;
sclk_cnt <= '0;
sclk_posedge <= 1'b0;
end
else begin
sclk_posedge <= 1'b0;
sclk_cnt <= sclk_cnt + 1'b1;
if (sclk_cnt == SCLK_CW'(SCLK_PERIOD/2))
spi_clk_o <= 1'b0;
else
if (sclk_cnt == SCLK_CW'(SCLK_PERIOD-1)) begin
spi_clk_o <= 1'b1;
sclk_cnt <= '0;
sclk_posedge <= 1'b1;
end
end
/* Sample rate need more accuracy */
localparam SRATE_PERIOD = $rtoi($floor($itor(CLOCK_FREQ)/$itor(SAMPLE_RATE) + 0.5));
localparam SRATE_CW = $clog2(SRATE_PERIOD);
logic [SRATE_CW-1:0] srate_cnt;
logic sample;
always_ff @(posedge clock, posedge reset)
if (reset) begin
sample <= 1'b0;
srate_cnt <= '0;
end
else
if (srate_cnt == SRATE_CW'(SRATE_PERIOD-1)) begin
sample <= 1'b1;
srate_cnt <= '0;
end
else begin
sample <= 1'b0;
srate_cnt <= srate_cnt + 1'b1;
end
/* Receive data FSM */
enum int unsigned {
ST_RELAX = 0,
ST_SHIFT,
ST_STROBE
} state;
logic [3:0] bit_cnt;
logic [11:0] data_sr[CHANNELS];
integer i;
always_ff @(posedge clock, posedge reset)
if (reset) begin
state <= ST_RELAX;
bit_cnt <= '0;
spi_ssn_o <= 1'b1;
strb_o <= 1'b0;
data_o <= '0;
for (i = 0; i < CHANNELS; i ++)
data_sr[i] <= '0;
end
else begin
strb_o <= 1'b0;
case (state)
ST_RELAX:
if (sample) begin
bit_cnt <= '0;
spi_ssn_o <= 1'b0;
state <= ST_SHIFT;
end
ST_SHIFT:
if (sclk_posedge) begin
for (i = 0; i < CHANNELS; i ++)
data_sr[i] <= { data_sr[i][10:0], spi_miso_i[i] };
bit_cnt <= bit_cnt + 1'b1;
if (bit_cnt == 4'd14) begin
spi_ssn_o <= 1'b1;
state <= ST_STROBE;
end
end
ST_STROBE: begin
for (i = 0; i < CHANNELS; i ++)
data_o[i*12 +: 12] <= data_sr[i];
strb_o <= 1'b1;
state <= ST_RELAX;
end
endcase
end
endmodule // mcp3201_ma

77
source/pll.sv Normal file
View File

@ -0,0 +1,77 @@
`timescale 1ns/100ps
/**
* PLL configuration 12MHz->30MHz
*
* F_PLLOUT: 30.000 MHz (requested)
* F_PLLOUT: 30.000 MHz (achieved)
*
* FEEDBACK: SIMPLE
* F_PFD: 12.000 MHz
* F_VCO: 960.000 MHz
*
* DIVR: 0 (4'b0000)
* DIVF: 79 (7'b1001111)
* DIVQ: 5 (3'b101)
*
* FILTER_RANGE: 1 (3'b001)
*/
`ifdef VERILATOR
`define TESTBENCH
`endif
module pll
(input clock_in,
output clock_out,
output locked);
wire unused_0, unused_1;
`ifdef TESTBENCH
`ifdef VERILATOR
/* In Verilator just forward clock_in to clock_out */
assign clock_out = clock_in;
assign locked = 1'b1;
`else // !VERILATOR
/* In Icarus Verilog generate new clock and 'locked' signal */
logic clock_tb;
logic lock_tb;
assign clock_out = clock_tb;
assign locked = lock_tb;
initial begin
clock_tb = 1'b0;
lock_tb = 1'b0;
repeat (100) @(posedge clock_tb);
lock_tb = 1'b1;
end
always #(33ns/2) clock_tb <= ~clock_tb;
`endif
`else
/* In HW use PLL primitive */
SB_PLL40_PAD #(.FEEDBACK_PATH("SIMPLE"),
.DIVR(4'd0),
/* For 30 MHz: DIVF=79, DIVQ=5
* For 50 MHz: DIVF=66, DIVQ=4 */
.DIVF(7'd79),
.DIVQ(3'd5),
.FILTER_RANGE(3'd1))
uut (.PACKAGEPIN (clock_in),
.PLLOUTGLOBAL (clock_out),
.EXTFEEDBACK (1'b0),
.DYNAMICDELAY (8'b0),
.LOCK (locked),
.BYPASS (1'b0),
.RESETB (1'b1),
.LATCHINPUTVALUE(1'b0),
.PLLOUTCORE (unused_0),
.SDO (unused_1),
.SDI (1'b0),
.SCLK (1'b0));
`endif
endmodule

24
source/pll_lock_reset.sv Normal file
View File

@ -0,0 +1,24 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
module pll_lock_reset #(parameter RESET_LEN = 8)
(input wire pll_clock,
input wire pll_lock,
output wire reset);
initial begin
`assert(RESET_LEN > 1);
end
logic [RESET_LEN:0] rst_sr;
initial rst_sr = '0;
always_ff @(posedge pll_clock, negedge pll_lock)
if (~pll_lock) rst_sr <= '0;
else rst_sr <= { 1'b1, rst_sr[RESET_LEN:1] };
assign reset = ~rst_sr[0];
endmodule // pll_lock_reset

256
source/quadrant_256.rom Normal file
View File

@ -0,0 +1,256 @@
00ff
02ff
03ff
05ff
06ff
08ff
09ff
0bff
0dff
0eff
10ff
11fe
13fe
14fe
16fe
17fe
19fe
1bfe
1cfd
1efd
1ffd
21fd
22fd
24fc
25fc
27fc
29fc
2afc
2cfb
2dfb
2ffb
30fa
32fa
33fa
35f9
36f9
38f9
39f8
3bf8
3cf8
3ef7
3ff7
41f7
43f6
44f6
46f5
47f5
49f4
4af4
4cf4
4df3
4ff3
50f2
51f2
53f1
54f1
56f0
57f0
59ef
5aee
5cee
5ded
5fed
60ec
62ec
63eb
64ea
66ea
67e9
69e8
6ae8
6ce7
6de7
6ee6
70e5
71e4
73e4
74e3
75e2
77e2
78e1
7ae0
7bdf
7cdf
7ede
7fdd
80dc
82dc
83db
84da
86d9
87d8
88d7
8ad7
8bd6
8cd5
8ed4
8fd3
90d2
92d1
93d0
94d0
95cf
97ce
98cd
99cc
9acb
9cca
9dc9
9ec8
9fc7
a1c6
a2c5
a3c4
a4c3
a5c2
a7c1
a8c0
a9bf
aabe
abbd
acbc
aebb
afba
b0b9
b1b8
b2b7
b3b5
b4b4
b5b3
b7b2
b8b1
b9b0
baaf
bbae
bcac
bdab
beaa
bfa9
c0a8
c1a7
c2a5
c3a4
c4a3
c5a2
c6a1
c79f
c89e
c99d
ca9c
cb9a
cc99
cd98
ce97
cf95
d094
d093
d192
d290
d38f
d48e
d58c
d68b
d78a
d788
d887
d986
da84
db83
dc82
dc80
dd7f
de7e
df7c
df7b
e07a
e178
e277
e275
e374
e473
e471
e570
e66e
e76d
e76c
e86a
e869
e967
ea66
ea64
eb63
ec62
ec60
ed5f
ed5d
ee5c
ee5a
ef59
f057
f056
f154
f153
f251
f250
f34f
f34d
f44c
f44a
f449
f547
f546
f644
f643
f741
f73f
f73e
f83c
f83b
f839
f938
f936
f935
fa33
fa32
fa30
fb2f
fb2d
fb2c
fc2a
fc29
fc27
fc25
fc24
fd22
fd21
fd1f
fd1e
fd1c
fe1b
fe19
fe17
fe16
fe14
fe13
fe11
ff10
ff0e
ff0d
ff0b
ff09
ff08
ff06
ff05
ff03
ff02

323
source/sugar_lissajous.sv Normal file
View File

@ -0,0 +1,323 @@
`timescale 1ns/100ps
`default_nettype none
/* verilator lint_off UNDRIVEN */
/* verilator lint_off UNUSED */
`define VARIANT_1
//`define VARIANT_2
module sugar_lissajous
(input wire CLK12,
output wire LED_R_N,
output wire LED_G_N,
output wire LED_B_N,
// PMOD 1: two ADC MCP3201
output wire P1_1, // R SSN
input wire P1_2, // --
input wire P1_3, // R DAT
output wire P1_4, // R CLK
output wire P1_9, // L CLK
input wire P1_10, // L DAT
input wire P1_11, // --
output wire P1_12, // L SSN
// PMOD 2: LCD
input wire P2_1, //
input wire P2_2, //
output wire P2_3, // DC
input wire P2_4, //
output wire P2_9, // CLK
input wire P2_10, //
output wire P2_11, // MOSI
output wire P2_12 // CSN
);
`ifdef VARIANT_1
localparam POINTS_COUNT = 128;
localparam POINTS_FADING = 1;
localparam DECIMATION = 200;
localparam ANGLE_INCREMENT = 8;
`elsif VARIANT_2
localparam POINTS_COUNT = 32;
localparam POINTS_FADING = 0;
localparam DECIMATION = 50;
localparam ANGLE_INCREMENT = 30;
`endif
assign LED_R_N = 1'b1;
assign LED_G_N = 1'b1;
assign LED_B_N = 1'b1;
logic clock;
logic reset;
logic pll_lock;
pll pll_i
(.clock_in(CLK12),
.clock_out(clock),
.locked(pll_lock));
pll_lock_reset #(.RESET_LEN(8)) reset_i
(.pll_clock(clock),
.pll_lock(pll_lock),
.reset(reset));
localparam SPI_SCLK_FREQ = 1000000;
localparam SAMPLE_RATE = 20000;
logic [11:0] rdata;
logic [11:0] ldata;
logic strb;
logic adc_ssn, adc_clk;
logic radc_dat, ladc_dat;
assign P1_1 = adc_ssn;
assign P1_4 = adc_clk;
assign P1_12 = adc_ssn;
assign P1_9 = adc_clk;
assign radc_dat = P1_3;
assign ladc_dat = P1_10;
/* Grab audio */
mcp3201_ma #(.CHANNELS(2),
.CLOCK_FREQ(30000000),
.SCLK_FREQ(SPI_SCLK_FREQ),
.SAMPLE_RATE(SAMPLE_RATE)) adcs_i
(.clock, .reset,
.spi_clk_o(adc_clk),
.spi_ssn_o(adc_ssn),
.spi_miso_i({ radc_dat, ladc_dat }),
.data_o({ rdata, ldata }),
.strb_o(strb));
/* Filter audio stream */
logic [15:0] fir_i;
logic [15:0] fir_o;
logic fir_i_ready;
logic fir_o_valid;
logic [15:0] fir_abs;
logic fir_strb;
`ifdef TESTBENCH
always_ff @ (posedge clock)
if (strb) begin
fir_abs <= 1024/2;
fir_strb <= 1'b1;
end
else
fir_strb <= 1'b0;
`else // !TESTBENCH
fir_filter fir_impl
(.clock, .reset,
.data_i(fir_i),
.data_o(fir_o),
.ready_i(fir_i_ready),
.valid_o(fir_o_valid));
always_ff @ (posedge clock, posedge reset)
if (reset)
fir_i_ready <= 1'b0;
else
if (~fir_i_ready) begin
fir_strb <= 1'b0;
if (strb) begin
fir_i <= (ldata >= 1024) ? 16'(ldata) - 16'd1024 : 16'd64512 - 16'(ldata);
fir_i_ready <= 1'b1;
end
end
else
if (fir_o_valid) begin
fir_abs <= fir_o[15] ? 16'hffff - fir_o + 1'b1 : fir_o;
fir_i_ready <= 1'b0;
fir_strb <= 1'b1;
end
`endif
/* Make frame redraw tick */
logic tick_redraw;
tick_generator #(.PERIOD(600000))
tick_refresh_gen (.clock, .reset, .tick_o(tick_redraw));
logic redraw;
logic redraw_done;
always_ff @ (posedge clock)
if (redraw_done)
redraw <= 1'b0;
else
if (tick_redraw)
redraw <= 1'b1;
/* LCD connection */
logic spi_csn, spi_clk, spi_sdo, dcn;
assign P2_12 = spi_csn;
assign P2_9 = spi_clk;
assign P2_11 = spi_sdo;
assign P2_3 = dcn;
logic [7:0] lcd_x;
logic [8:0] lcd_y;
logic lcd_req;
logic lcd_ack;
logic lcd_wr;
logic [15:0] color_w;
logic [15:0] color_r;
lcd_top #(.SPI_CLK_PERIOD(4)) lcd_i
(.clock, .reset,
.lcd_spi_csn_o(spi_csn),
.lcd_spi_clk_o(spi_clk),
.lcd_spi_dat_o(spi_sdo),
.lcd_spi_dcn_o(dcn),
.redraw_i(redraw),
.done_o(redraw_done),
.x_i(lcd_x),
.y_i(lcd_y),
.color_i(color_w),
.color_o(color_r),
.req_i(lcd_req),
.ack_o(lcd_ack),
.wr_i(lcd_wr));
/* Figure drawer */
logic [7:0] fig_x;
logic [8:0] fig_y;
logic [7:0] fig_h;
logic [7:0] fig_s;
logic [7:0] fig_v;
logic fig_req;
logic fig_ack;
assign lcd_wr = 1'b1;
fig_drawer fig_i
(.clock, .reset,
.x_i(fig_x),
.y_i(fig_y),
.h_i(fig_h),
.s_i(fig_s),
.v_i(fig_v),
.req_i(fig_req),
.ack_o(fig_ack),
.fb_x_o(lcd_x),
.fb_y_o(lcd_y),
.fb_color_o(color_w),
.fb_req_o(lcd_req),
.fb_ack_i(lcd_ack));
/* Awesome circle */
logic [9:0] cir_angle;
logic [7:0] cir_x;
logic [7:0] cir_y;
logic cir_req;
logic cir_ack;
circle_1024 DUT
(.clock, .reset,
.angle(cir_angle),
.r(fir_abs[11:4]),
.x0(8'd120),
.y0(8'd128),
.x(cir_x),
.y(cir_y),
.req_i(cir_req),
.ack_o(cir_ack));
/* Points ring buffer */
logic [7:0] pt_x;
logic [7:0] pt_y;
logic [7:0] pt_h;
logic pt_req;
logic pt_ack;
fig_ring #(.POINT_COUNT(POINTS_COUNT),
.FADING(POINTS_FADING)) fig_ring_i
(.clock, .reset,
.pt_x(pt_x),
.pt_y(pt_y),
.pt_h(pt_h),
.pt_req_i(pt_req),
.pt_ack_o(pt_ack),
.fig_x_o(fig_x),
.fig_y_o(fig_y),
.fig_h_o(fig_h),
.fig_s_o(fig_s),
.fig_v_o(fig_v),
.fig_req_o(fig_req),
.fig_ack_i(fig_ack));
/* Decimate audio stream */
localparam DECIMATION_CW = $clog2(DECIMATION);
logic [DECIMATION_CW-1:0] decim_cntr;
logic decim_strobe;
always_ff @ (posedge clock, posedge reset)
if (reset)
decim_cntr <= '0;
else
if (decim_cntr == (DECIMATION-1)) begin
decim_cntr <= '0;
decim_strobe <= 1'b1;
end
else begin
decim_strobe <= 1'b0;
if (fir_strb)
decim_cntr <= decim_cntr + 1'b1;
end
/* Draw autio sample on circle */
enum int unsigned {
ST_FIG_IDLE = 0,
ST_FIG_CIRCLE,
ST_FIG_DRAW
} state_fig;
always_ff @ (posedge clock, posedge reset)
if (reset) begin
state_fig <= ST_FIG_IDLE;
cir_req <= 1'b0;
pt_req <= 1'b0;
cir_angle <= '0;
pt_h <= '0;
end
else
case (state_fig)
ST_FIG_IDLE:
if (decim_strobe) begin
cir_req <= 1'b1;
state_fig <= ST_FIG_CIRCLE;
end
ST_FIG_CIRCLE:
if (cir_ack) begin
cir_req <= 1'b0;
pt_x <= cir_x;
pt_y <= cir_y;
pt_req <= 1'b1;
state_fig <= ST_FIG_DRAW;
end
ST_FIG_DRAW:
if (pt_ack) begin
pt_req <= 1'b0;
cir_angle <= cir_angle + ANGLE_INCREMENT;
pt_h <= pt_h + 1'b1;
state_fig <= ST_FIG_IDLE;
end
endcase
endmodule // sugar_lissajous

33
source/tick_generator.sv Normal file
View File

@ -0,0 +1,33 @@
`timescale 1ns/100ps
`default_nettype none
`include "assert.vh"
module tick_generator #(parameter PERIOD = 1000)
(input wire clock,
input wire reset,
output reg tick_o);
initial begin
`assert(PERIOD > 1);
end
localparam TICK_CW = $clog2(PERIOD);
logic [TICK_CW-1:0] cntr;
always_ff @(posedge clock, posedge reset)
if (reset) begin
cntr <= '0;
tick_o <= 1'b0;
end
else begin
if (cntr == (PERIOD-1)) begin
cntr <= '0;
tick_o <= 1'b1;
end
else begin
cntr <= cntr + 1'b1;
tick_o <= 1'b0;
end
end
endmodule // tick_generator

5
testbench/.dir-locals.el Normal file
View File

@ -0,0 +1,5 @@
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((verilog-mode . ((flycheck-verilator-include-path . ("." "../source"))
(verilog-library-directories . ("." "../source")))))

5
testbench/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.out
*.bin
*.vcd
*.gtkw
filtered.txt

31
testbench/Makefile Normal file
View File

@ -0,0 +1,31 @@
VC = iverilog
VI = vvp
#SOURCES = ../source/lcd_320x240_spi.sv
SOURCES = $(wildcard ../source/*.sv)
SOURCES += ../../local/share/yosys/ice40/cells_sim.v
VFLAGS = -g2012 -I../source
TBS = $(wildcard tb_*.sv)
DEFINES = -D TESTBENCH
VCDDEPS = $(TBS:.sv=.vcd)
BINDEPS = $(TBS:.sv=.bin)
all: $(VCDDEPS)
.SECONDARY:
#.SILENT: $(VCDDEPS) $(BINDEPS) clean
%.vcd: %.bin
@echo "Simulate :" $(<:.bin=.sv)
$(VI) $< #> $(<:.bin=.out)
%.bin: %.sv $(SOURCES)
@echo "Compile :" $(@:.bin=.sv)
$(VC) $(VFLAGS) $(DEFINES) -D DUMPFILE=\"$(@:.bin=.vcd)\" -o $@ $< $(SOURCES)
clean:
@echo "Remove *.bin, *.vcd, *.out"
rm -rf *.bin
rm -rf *.out
rm -rf *.vcd

View File

@ -0,0 +1 @@
../source/fig_circle_8x8.rom

9
testbench/fir-magn.m Normal file
View File

@ -0,0 +1,9 @@
pkg load signal
data = csvread("filtered.txt");
data = data(8:length(data));
spec = abs(fft(data ./ 32768));
len = length(data);
x = 1:len/2;
plot(x ./ len, mag2db(spec(1:len/2)));

View File

@ -0,0 +1 @@
../source/fir_425_50hz_100hz_0db_40db.rom

View File

@ -0,0 +1,2 @@
-I./obj_dir
-I/usr/share/verilator/include

View File

@ -0,0 +1,5 @@
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((verilog-mode . ((flycheck-verilator-include-path . ("." "../../source"))
(verilog-library-directories . ("." "../../source")))))

2
testbench/lcd-model/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
obj_dir
*.vcd

View File

@ -0,0 +1,31 @@
SOURCE_DIR = ../../source
SOURCES = testbench_top.cpp \
testbench_top.sv \
lcd_ili9341_4spi.sv \
$(SOURCE_DIR)/sugar_lissajous.sv \
$(SOURCE_DIR)/pll_lock_reset.sv \
$(SOURCE_DIR)/pll.sv \
$(SOURCE_DIR)/mcp3201_ma.sv \
$(SOURCE_DIR)/lfsr.sv \
$(SOURCE_DIR)/lcd_top.sv \
$(SOURCE_DIR)/lcd_spi.sv \
$(SOURCE_DIR)/ice40_spram.sv \
$(SOURCE_DIR)/ice40_mac16x16.sv
SOURCES += ../../../local/share/yosys/ice40/cells_sim.v
TOP_MODULE = testbench_top
FLAGS = -DTESTBENCH -Wno-WIDTH -cc -I$(SOURCE_DIR) --top-module $(TOP_MODULE) +1800-2017ext+sv -I$(SOURCE_DIR)
#FLAGS += --threads 8
FLAGS += --trace
all: $(SOURCES)
verilator $(FLAGS) --exe --build -o $(TOP_MODULE) $(SOURCES)
pre:
verilator $(FLAGS) -o $(TOP_MODULE) $(SOURCES)
clean:
rm -rf obj_dir

View File

@ -0,0 +1 @@
../../source/fig_circle_8x8.rom

View File

@ -0,0 +1,80 @@
#! /usr/bin/env racket
#lang racket/gui
(require racket/gui/base)
(define display-width 240)
(define display-height 320)
(define fb-size (* display-width display-height))
(define (set-pixel! fb x y r g b)
(let ((i (* 4 (+ x (* y display-width)))))
(bytes-set! fb (+ i 0) 255)
(bytes-set! fb (+ i 1) r)
(bytes-set! fb (+ i 2) g)
(bytes-set! fb (+ i 3) b)))
(define (clear-screen fb r g b)
(for-each
(lambda (n)
(let ((n (* n 4)))
(bytes-set! fb (+ n 0) 255)
(bytes-set! fb (+ n 1) r)
(bytes-set! fb (+ n 2) g)
(bytes-set! fb (+ n 3) b)))
(range fb-size)))
;;; MAIN
(let* ((frame-buffer (make-bytes (* fb-size 4)))
(frame-bitmap (make-bitmap display-width display-height))
(frame (new frame%
(label "LCD")
(min-width display-width)
(min-height display-height)
(stretchable-width #f)
(stretchable-height #f)))
(canvas (new canvas% (parent frame)
(paint-callback
(lambda (canvas dc)
(send frame-bitmap
set-argb-pixels 0 0
display-width display-height
frame-buffer)
(send dc draw-bitmap frame-bitmap 0 0)))))
(cmdl (current-command-line-arguments))
(pipe (if (zero? (vector-length cmdl)) #f (vector-ref cmdl 0))))
(clear-screen frame-buffer 50 100 150)
(send frame show #t)
;; Read pixels data
(thread (λ ()
(let ((thunk
(lambda ()
(let loop ()
(let ((s (read-line)))
(if (eof-object? s)
(loop) ;; (send frame show #f)
(let ((l (string-split s)))
(when (= 5 (length l))
(let* ((x (string->number (list-ref l 0)))
(y (string->number (list-ref l 1)))
(r (string->number (list-ref l 2)))
(g (string->number (list-ref l 3)))
(b (string->number (list-ref l 4))))
(set-pixel! frame-buffer x y r g b)))
(loop))))))))
(if pipe
(with-input-from-file "lcd_pipe" thunk)
(thunk)))))
;; Refresh screen
(thread (lambda ()
(let loop ()
(send canvas refresh)
(sleep 0.02)
(loop)))))

View File

@ -0,0 +1,156 @@
`timescale 1ns/100ps
`default_nettype none
module lcd_ili9341_4spi
(input wire clock,
input wire reset,
input wire csn_i,
input wire clk_i,
input wire sdi_i,
input wire dcn_i,
output int x_o,
output int y_o,
output logic [7:0] r_o,
output logic [7:0] g_o,
output logic [7:0] b_o,
output logic strobe_o);
logic [7:0] readed;
logic [7:0] spi_sr;
int bit_cntr;
logic clk_prev;
logic rstrobe;
always_ff @ (posedge clock) clk_prev <= clk_i;
always_ff @(posedge clock, posedge csn_i)
if (csn_i || reset) begin
bit_cntr <= 0;
end
else begin
if (clk_prev == 1'b0 &&
clk_i == 1'b1)
begin
spi_sr <= { spi_sr[6:0], sdi_i };
bit_cntr <= bit_cntr + 1;
end
if (bit_cntr == 8) begin
readed <= spi_sr;
rstrobe <= 1'b1;
bit_cntr <= 0;
end
else
rstrobe <= 1'b0;
end
enum int unsigned {
ST_IDLE = 0,
ST_CADDR,
ST_PADDR,
ST_MEM_WRITE
} state;
logic [15:0] x_beg;
logic [15:0] x_end;
logic [15:0] y_beg;
logic [15:0] y_end;
initial begin
x_beg = 0;
x_end = 239;
y_beg = 0;
y_end = 319;
end
int n;
int x, y;
logic [7:0] tmp;
always_ff @ (posedge clock, posedge csn_i)
if (csn_i || reset) begin
state <= ST_IDLE;
n <= 0;
strobe_o <= 1'b0;
end
else begin
strobe_o <= 1'b0;
if (rstrobe) begin
if (~dcn_i) begin
case (readed)
8'h2a: state <= ST_CADDR;
8'h2b: state <= ST_PADDR;
8'h2c: begin
x <= int'(x_beg);
y <= int'(y_beg);
state <= ST_MEM_WRITE;
end
default: begin end
endcase
n <= 0;
end
else
case (state)
ST_CADDR: begin
n <= n + 1;
case (n)
0: x_beg[15:8] <= readed;
1: x_beg[7:0] <= readed;
2: x_end[15:8] <= readed;
3: x_end[7:0] <= readed;
endcase
end
ST_PADDR: begin
n <= n + 1;
case (n)
0: y_beg[15:8] <= readed;
1: y_beg[7:0] <= readed;
2: y_end[15:8] <= readed;
3: y_end[7:0] <= readed;
endcase
end
ST_MEM_WRITE: begin
if (n == 0) begin
n <= 1;
tmp <= readed;
end
else begin
n <= 0;
// $display("%d %d %d %d %d", x, y,
// { tmp[7:3], 1'b0 },
// { tmp[2:0], readed[7:5] },
// { readed[4:0], 1'b0 });
x_o <= x;
y_o <= y;
r_o <= { 2'b00, tmp[7:3], 1'b0 };
g_o <= { 2'b00, tmp[2:0], readed[7:5] };
b_o <= { 2'b00, readed[4:0], 1'b0 };
strobe_o <= 1'b1;
x <= x + 1;
if (x == int'(x_end)) begin
x <= int'(x_beg);
y <= y + 1;
if (y == int'(y_end))
y <= int'(y_beg);
end
end
end
endcase
end
end
endmodule // lcd_ili9341_4spi

View File

@ -0,0 +1,61 @@
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000
000

View File

@ -0,0 +1 @@
../../source/quadrant_256.rom

View File

@ -0,0 +1,107 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <verilated_vcd_c.h>
#include "Vtestbench_top.h"
#define DUMPFILE "testbench_top.vcd"
#define PIPE_FILE "lcd_pipe"
/* Clock period in timescale units
* In datapath.sv uses 100ps time unit */
#define CLOCK_PERIOD 2
#define TIMESCALE 20000
/* Simulation time */
uint64_t simtime = 0;
/* Clock cycle counter */
uint64_t cycle = 0;
/* Called by $time in Verilog */
double sc_time_stamp() {
return simtime;
}
int main(int argc, char **argv)
{
Verilated::commandArgs(argc, argv);
/* Create model instance */
Vtestbench_top *dp = new Vtestbench_top;
/* Enable trace if compiled with --trace flag */
#if (VM_TRACE == 1)
VerilatedVcdC *vcd = NULL;
const char* trace_flag = Verilated::commandArgsPlusMatch("trace");
if (trace_flag && (strcmp(trace_flag, "+trace") == 0))
{
Verilated::traceEverOn(true);
vcd = new VerilatedVcdC;
dp->trace(vcd, 99);
vcd->open(DUMPFILE);
}
#endif
/* Open pipe */
FILE *o_file = fopen(PIPE_FILE, "w");
if (!o_file) {
printf("ERROR: Can't open file/pipe '%s'\n", PIPE_FILE);
delete dp;
return -1;
}
int posedge_clock = 0;
int data_loops = 6;
uint64_t check_cycle;
/* Initial */
dp->reset = 1;
dp->clock = 0;
while (!Verilated::gotFinish())
{
posedge_clock = 0;
if ((simtime % (CLOCK_PERIOD/2)) == 0) {
dp->clock = !dp->clock;
if (dp->clock) {
posedge_clock = 1;
cycle ++;
}
}
/* release reset at 200 simulation cycle */
if (simtime == 200) dp->reset = 0;
dp->eval();
/* ouput data */
if (posedge_clock && !dp->reset && dp->strobe)
fprintf(o_file, "%i %i %i %i %i\n",
dp->x, dp->y, dp->r << 2, dp->g << 2, dp->b << 2);
#if (VM_TRACE == 1)
if (vcd)
vcd->dump(simtime * TIMESCALE);
#endif
simtime ++;
}
dp->final();
printf("[%lu] Stop simulation\n", simtime);
#if (VM_TRACE == 1)
if (vcd) vcd->close();
#endif
fclose(o_file);
delete dp;
return 0;
}

View File

@ -0,0 +1,41 @@
`timescale 1ns/100ps
`default_nettype none
/* verilator lint_off PINMISSING */
module testbench_top
(input wire clock,
input wire reset,
output int x,
output int y,
output [7:0] r,
output [7:0] g,
output [7:0] b,
output strobe);
logic csn, mosi, clk, dcn;
sugar_lissajous DUT
(.CLK12(clock),
.P1_3(1'b0),
.P1_10(1'b1),
.P2_3(dcn),
.P2_9(clk),
.P2_11(mosi),
.P2_12(csn));
lcd_ili9341_4spi LCD
(.clock, .reset,
.csn_i(csn),
.clk_i(clk),
.sdi_i(mosi),
.dcn_i(dcn),
.x_o(x),
.y_o(y),
.r_o(r),
.g_o(g),
.b_o(b),
.strobe_o(strobe));
endmodule // testbench_top

1
testbench/lcd_init.rom Symbolic link
View File

@ -0,0 +1 @@
../source/lcd_init.rom

1
testbench/quadrant_256.rom Symbolic link
View File

@ -0,0 +1 @@
../source/quadrant_256.rom

60
testbench/tb_circle.sv Normal file
View File

@ -0,0 +1,60 @@
`timescale 1ns/100ps
module tb_circle;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic [9:0] angle;
logic [7:0] r;
logic [7:0] x0;
logic [7:0] y0;
logic [7:0] x;
logic [7:0] y;
logic req, ack;
circle_1024 DUT
(.clock, .reset,
.angle,
.r,
.x0,
.y0,
.x,
.y,
.req_i(req),
.ack_o(ack));
initial begin
reset = 1'b1;
req = 1'b0;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
@(posedge clock) #1;
angle = '0;
r = 120;
x0 = 120;
y0 = 128;
for (int i = 0; i < 1024; i ++) begin
@(posedge clock) #1;
req = 1'b1;
wait (ack);
angle = angle + 1'b1;
end
req = 1'b0;
repeat(10) @(posedge clock) #1;
$finish;
end
initial begin
$dumpfile("tb_circle.vcd");
$dumpvars;
end
endmodule // tb_circle

View File

@ -0,0 +1,70 @@
`timescale 1ns/100ps
module tb_fig_drawer;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic [7:0] x;
logic [8:0] y;
logic [7:0] h, s, v;
logic req, ack;
logic [7:0] fb_x;
logic [8:0] fb_y;
logic [15:0] fb_color;
logic fb_req, fb_ack;
fig_drawer DUT
(.clock, .reset,
.x_i(x), .y_i(y),
.h_i(h), .s_i(s), .v_i(v),
.req_i(req), .ack_o(ack),
.fb_x_o(fb_x),
.fb_y_o(fb_y),
.fb_color_o(fb_color),
.fb_req_o(fb_req),
.fb_ack_i(fb_ack));
int fb_lat_n;
always_ff @ (posedge clock) begin
fb_ack <= 1'b0;
if (fb_req)
if (fb_lat_n == 2) begin
fb_ack <= 1'b1;
fb_lat_n <= 0;
end
else fb_lat_n <= fb_lat_n + 1;
end
always_ff @ (posedge clock)
if (ack)
req <= 1'b0;
initial begin
reset <= 1'b1;
repeat(10) @(posedge clock);
reset <= 1'b0;
@(posedge clock);
x <= 'd20;
y <= 'd50;
h <= 50;
s <= 100;
v <= 150;
req <= 1'b1;
repeat(1000) @(posedge clock);
$finish;
end
initial begin
$dumpfile("tb_fig_drawer.vcd");
$dumpvars;
end
endmodule // tb_fig_drawer

69
testbench/tb_fig_ring.sv Normal file
View File

@ -0,0 +1,69 @@
`timescale 1ns/100ps
module tb_fig_ring;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic pt_ack_o;
logic [7:0] fig_x_o;
logic [8:0] fig_y_o;
logic [7:0] fig_h_o;
logic [7:0] fig_s_o;
logic [7:0] fig_v_o;
logic fig_req_o;
logic [7:0] pt_x;
logic [7:0] pt_y;
logic [7:0] pt_h;
logic pt_req_i;
logic fig_ack_i;
fig_ring DUT (/*AUTOINST*/
// Outputs
.pt_ack_o (pt_ack_o),
.fig_x_o (fig_x_o[7:0]),
.fig_y_o (fig_y_o[8:0]),
.fig_h_o (fig_h_o[7:0]),
.fig_s_o (fig_s_o[7:0]),
.fig_v_o (fig_v_o[7:0]),
.fig_req_o (fig_req_o),
// Inputs
.clock (clock),
.reset (reset),
.pt_x (pt_x[7:0]),
.pt_y (pt_y[7:0]),
.pt_h (pt_h[7:0]),
.pt_req_i (pt_req_i),
.fig_ack_i (fig_ack_i));
assign fig_ack_i = 1'b1;
always_ff @ (posedge clock)
if (pt_ack_o)
pt_req_i <= 1'b0;
initial begin
reset = 1'b1;
pt_req_i = 1'b0;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
@(posedge clock) #1;
pt_x = 0;
pt_y = 0;
pt_h = 100;
pt_req_i = 1'b1;
repeat(1000) @(posedge clock) #1;
$finish;
end
initial begin
$dumpfile("tb_fig_ring.vcd");
$dumpvars;
end
endmodule // tb_fig_ring

View File

@ -0,0 +1,66 @@
`timescale 1ns/100ps
module tb_fir_filter;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic signed [15:0] data_i;
logic signed [15:0] data_o;
logic input_ready, output_valid;
localparam FILTER_LEN = 425;
fir_filter #(.LEN(FILTER_LEN),
.COEFFS_ROM_FILE("fir_425_50hz_100hz_0db_40db.rom")) DUT
(.clock, .reset,
.data_i, .data_o,
.ready_i(input_ready),
.valid_o(output_valid));
event done;
integer file_o;
initial begin
file_o = $fopen("filtered.txt", "w");
@(done);
$fclose(file_o);
$finish;
end
initial begin
reset = 1'b1;
input_ready = 1'b0;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
@(posedge clock) #1;
data_i = 16'd32767;
input_ready = 1'b1;
@(posedge clock) #1;
wait (output_valid)
$fdisplay(file_o, "%d", data_o);
data_i = '0;
for (int i = 1; i < FILTER_LEN; i ++) begin
@(posedge clock) #1;
wait (output_valid)
$fdisplay(file_o, "%d", data_o);
end
->done;
repeat(10) @(posedge clock) #1;
$finish;
end
initial begin
$dumpfile("tb_fir_filter.vcd");
$dumpvars;
end
endmodule // tb_fir_filter

49
testbench/tb_hsl2rgb.sv Normal file
View File

@ -0,0 +1,49 @@
`timescale 1ns/100ps
module tb_hsl2rgb;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic [7:0] h, s, l;
logic [7:0] r, g, b;
logic valid, ready;
hsl2rgb DUT
(.clock, .reset,
.h, .s, .l,
.ready_i(ready),
.r, .g, .b,
.valid_o(valid));
always_ff @ (posedge clock)
if (valid)
$display("%d %d %d", r, g, b);
initial begin
reset = 1'b1;
ready = 1'b0;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
@(posedge clock) #1;
h = 8'd128;
s = 8'd255;
l = 8'd130;
ready = 1'b1;
@(posedge clock) #1;
ready = 1'b0;
repeat(20) @(posedge clock);
$finish;
end
initial begin
$dumpfile("tb_hsl2rgb.vcd");
$dumpvars;
end
endmodule // tb_hsl2rgb

78
testbench/tb_hsv2rgb.sv Normal file
View File

@ -0,0 +1,78 @@
`timescale 1ns/100ps
module tb_hsv2rgb;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 100MHz (10ns period) */
always #(10ns/2) clock <= ~clock;
logic [7:0] h, s, v;
logic [7:0] r, g, b;
logic valid, ready;
hsv2rgb DUT
(.clock, .reset,
.h, .s, .v,
.ready_i(ready),
.r, .g, .b,
.valid_o(valid));
always_ff @ (posedge clock)
if (valid)
$display("%d %d %d", r, g, b);
initial begin
reset = 1'b1;
ready = 1'b0;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
@(posedge clock) #1;
h = 8'd50;
s = 8'd100;
v = 8'd150;
ready = 1'b1;
@(posedge clock) #1;
h = 8'dx;
s = 8'dx;
v = 8'dx;
ready = 1'b0;
@(posedge clock) #1;
@(posedge clock) #1;
@(posedge clock) #1;
@(posedge clock) #1;
h = 8'd50;
s = 8'd100;
v = 8'd150;
ready = 1'b1;
@(posedge clock) #1;
h = 8'd111;
s = 8'd222;
v = 8'd33;
ready = 1'b1;
@(posedge clock) #1;
h = 8'd200;
s = 8'd150;
v = 8'd50;
ready = 1'b1;
@(posedge clock) #1;
ready = 1'b0;
repeat(10) @(posedge clock);
$finish;
end
initial begin
$dumpfile("tb_hsv2rgb.vcd");
$dumpvars;
end
endmodule // tb_hsv2rgb

63
testbench/tb_lcd_spi.sv Normal file
View File

@ -0,0 +1,63 @@
`timescale 1ns/100ps
module tb_lcd_spi;
logic clock = 1'b0;
logic reset = 1'b1;
/* Master clock 50MHz (20ns period) */
always #(20ns/2) clock <= ~clock;
logic [7:0] data;
logic push, done;
logic sclk, sdo;
lcd_spi #(.DATA_WIDTH(8),
.SPI_CLK_PERIOD(16)) DUT
(.clock, .reset,
.data_i(data),
.push_i(push),
.done_o(done),
.spi_clk_o(sclk),
.spi_dat_o(sdo));
int state;
always_ff @(posedge clock)
if (reset) begin
push <= 1'b0;
state <= 0;
end
else begin
case (state)
0: begin
data <= $random;
push <= 1'b1;
state <= 1;
end
1: begin
if (done) begin
//data <= $random;
push <= 1'b0;
state <= 0;
end
end
endcase
end
initial begin
reset = 1'b1;
repeat(10) @(posedge clock) #1;
reset = 1'b0;
repeat(1000) @(posedge clock);
$finish;
end
initial begin
$dumpfile("tb_lcd_spi.vcd");
$dumpvars;
end
endmodule // tb_lcd_spi