gateware: Initial import of the FPGA gateware

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2023-03-11 23:54:12 +01:00
parent bf1310fd5d
commit c85dc29b06
27 changed files with 6205 additions and 0 deletions

12
.gitmodules vendored Normal file
View File

@ -0,0 +1,12 @@
[submodule "gateware/build"]
path = gateware/build
url = https://github.com/no2fpga/no2build
[submodule "gateware/cores/no2ice40"]
path = gateware/cores/no2ice40
url = https://github.com/no2fpga/no2ice40
[submodule "gateware/cores/no2misc"]
path = gateware/cores/no2misc
url = https://github.com/no2fpga/no2misc
[submodule "gateware/cores/no2usb"]
path = gateware/cores/no2usb
url = https://github.com/no2fpga/no2usb

60
gateware/Makefile Normal file
View File

@ -0,0 +1,60 @@
# Project config
PROJ=xmas-snoopy
PROJ_DEPS := no2ice40 no2misc no2usb
PROJ_RTL_SRCS := $(addprefix rtl/, \
led_ctrl.v \
picorv32_ice40_regs.v \
picorv32.v \
pmu.v \
soc_bram.v \
soc_picorv32_base.v \
soc_picorv32_bridge.v \
soc_spram.v \
soc_usb_buf_bridge.v \
sysmgr_1.v \
xclk_cnt.v \
xclk_pulse.v \
)
PROJ_SIM_SRCS := $(addprefix sim/, \
spiflash.v \
sysmgr_sim.v \
)
PROJ_SIM_SRCS += rtl/top.v
PROJ_TESTBENCHES=\
led_ctrl_tb \
top_tb \
$(NULL)
PROJ_PREREQ = \
$(BUILD_TMP)/boot.hex \
$(NULL)
PROJ_TOP_SRC := rtl/top.v
PROJ_TOP_MOD := top
# Target config
BOARD ?= xmas-snoopy
DEVICE := up5k
PACKAGE := sg48
# Toolchain options
YOSYS_SYNTH_ARGS := -dsp -dffe_min_ce_use 4
NEXTPNR_SEED ?= 1
NEXTPNR_ARGS := --no-promote-globals --seed $(NEXTPNR_SEED) --pre-pack data/clocks.py
ICEPACK_ARGS :=
# Include default rules
NO2BUILD_DIR ?= build
include $(NO2BUILD_DIR)/project-rules.mk
# Custom rules
fw_boot_build:
../firmware/boot/boot.hex: fw_boot_build
make -C ../firmware/boot boot.hex
$(BUILD_TMP)/boot.hex: ../firmware/boot/boot.hex
cp $< $@
.PHONY: fw_boot_build

1
gateware/build Submodule

@ -0,0 +1 @@
Subproject commit ab34622c8220a45f6b084b793a945e28eac2def4

@ -0,0 +1 @@
Subproject commit 97413633237ef5ad899f04e0b4b22679c5f6f6bb

@ -0,0 +1 @@
Subproject commit a494fc73cf5466f34ec040f3e9ab3e341a305114

1
gateware/cores/no2usb Submodule

@ -0,0 +1 @@
Subproject commit fdf42a6571a4ae49556626e6fffca1582796f7e8

4
gateware/data/clocks.py Normal file
View File

@ -0,0 +1,4 @@
ctx.addClock("clk_led", 6)
ctx.addClock("clk_sys", 24)
ctx.addClock("clk_usb", 48)
ctx.addClock("pmu_I.btn_clk", 20) # Just to avoid screwing the histogram

View File

@ -0,0 +1,49 @@
# SPI
set_io -nowarn -pullup yes spi_io[0] 14
set_io -nowarn -pullup yes spi_io[1] 17
set_io -nowarn -pullup yes spi_io[2] 20
set_io -nowarn -pullup yes spi_io[3] 13
set_io -nowarn -pullup yes spi_clk 15
set_io -nowarn -pullup yes spi_cs_n 16
# USB
set_io -nowarn -pullup no usb_dp 19
set_io -nowarn -pullup no usb_dn 18
set_io -nowarn -pullup no usb_pu 21
# Power
set_io -nowarn -pullup yes pwr_usb_n 45
set_io -nowarn -pullup yes pwr_chg_n 44
set_io -nowarn -pullup no pwr_off 47
# Buttons
set_io -nowarn -pullup yes btn[0] 46
set_io -nowarn -pullup yes btn[1] 48
# I2C
set_io -nowarn -pullup yes -pullup_resistor 10K scl 10
set_io -nowarn -pullup yes -pullup_resistor 10K sda 11
# Speaker
set_io -nowarn -pullup no hp_p 4
set_io -nowarn -pullup no hp_n 3
# LED matrix
set_io -nowarn -pullup no led_a[0] 42
set_io -nowarn -pullup no led_a[1] 43
set_io -nowarn -pullup no led_a[2] 23
set_io -nowarn -pullup no led_a[3] 25
set_io -nowarn -pullup no led_a[4] 26
set_io -nowarn -pullup no led_a[5] 27
set_io -nowarn -pullup no led_a[6] 28
set_io -nowarn -pullup no led_a[7] 31
set_io -nowarn -pullup no led_a[8] 32
set_io -nowarn -pullup no led_a[9] 34
set_io -nowarn -pullup no led_a[10] 35
set_io -nowarn -pullup no led_a[11] 36
set_io -nowarn -pullup no led_a[12] 37
set_io -nowarn -pullup no led_a[13] 38
set_io -nowarn led_c[0] 39
set_io -nowarn led_c[1] 40
set_io -nowarn led_c[2] 41

460
gateware/rtl/led_ctrl.v Normal file
View File

@ -0,0 +1,460 @@
/*
* led_ctrl.v
*
* vim: ts=4 sw=4
*
* LED controller
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module led_ctrl (
// LED matrix
output wire [13:0] led_a,
output wire [2:0] led_c,
input wire led_clk,
input wire led_rst,
// Status output
output reg trig_out,
// Wishbone
input wire [15:0] wb_addr,
output reg [31:0] wb_rdata,
input wire [31:0] wb_wdata,
input wire wb_we,
input wire wb_cyc,
output reg wb_ack,
input wire wb_clk,
input wire wb_rst
);
//
// 6 MHz LED clock
//
// 16 time slots of 129 cycles -> ~ 2900 Hz PWM rate
//
// For each time slot :
// - Anode sel : 4b
// - Start cycle : 7b \__ For each Cathode
// - Stop cycle : 7b / x3
//
// Both the 'Start' and 'Stop' cycle are counted as active
// so start=0 and stop=127 means active for 128 cycles.
// To be completely off jus set stop < start
//
// 256 entries in EBR = 16 frames of 16 time slots
// Each frame is played 96 times, leading to about ~ 30 fps
//
// Signals
// -------
// Bus access
wire bus_clr_rd;
wire bus_clr_wr;
reg bus_we_csr;
reg bus_we_mem;
// CSR
reg ctrl_scan_ena;
reg ctrl_drv_rgbleden;
reg ctrl_drv_curren;
reg [3:0] ctrl_trig_frame;
reg ctrl_trig_ena;
reg ctrl_trig_clr;
wire [3:0] stat_cur_frame;
// Frame memory
wire [7:0] fmw_addr;
wire [47:0] fmw_data;
wire [47:0] fmw_mask;
wire fmw_ena;
wire [7:0] fmr_addr;
wire [47:0] fmr_data;
wire fmr_ena;
// Control
reg [4:0] led_off_sync;
wire led_off_0;
wire led_off_1;
wire led_off_2;
// Trigger
wire trig_upd;
wire trig_clr;
reg [3:0] trig_frame;
reg trig_ena;
wire trig_match;
// Cycle counter
wire tick_0;
reg tick_1;
reg tick_2;
reg [7:0] cycle_cnt_0;
wire [6:0] cycle_0;
reg [6:0] cycle_1;
// Subframe counter
reg [3:0] subframe_0;
wire subframe_last_0;
wire subframe_ce_0;
// Play counter
reg [7:0] play_cnt_0;
wire play_last_0;
wire play_ce_0;
// Frame
reg [3:0] frame_0;
wire frame_ce_0;
// PWM
wire [6:0] cyc_start_1[0:2];
wire [6:0] cyc_stop_1[0:2];
reg [2:0] sig_start_1;
reg [2:0] sig_stop_1;
reg [2:0] started_2;
reg [2:0] stopped_2;
// Anode/Cathode
wire [3:0] led_a_1;
reg [13:0] led_a_2;
reg [2:0] led_c_3;
// Bus interface
// -------------
// Ack
always @(posedge wb_clk)
wb_ack <= wb_cyc & ~wb_ack;
// Clear signals
assign bus_clr_rd = wb_ack | ~wb_cyc;
assign bus_clr_wr = wb_ack | ~wb_cyc | ~wb_we;
// Reads
always @(posedge wb_clk)
if (bus_clr_rd)
wb_rdata <= 32'h00000000;
else
wb_rdata <= {
trig_out, /* [31] */
ctrl_trig_ena, /* [30] */
2'b00, /* [29:28] */
ctrl_trig_frame, /* [27:24] */
4'b0000, /* [23:20] */
stat_cur_frame, /* [19:16] */
13'h0000, /* [15: 3] */
ctrl_drv_curren, /* [ 2] */
ctrl_drv_rgbleden, /* [ 1] */
ctrl_scan_ena /* [ 0] */
};
xclk_cnt #(
.WIDTH(4)
) xclk_frame_I (
.i_val (frame_0),
.i_clk (led_clk),
.o_val (stat_cur_frame),
.o_clk (wb_clk),
.rst (led_clk | wb_clk)
);
// Write strobes
always @(posedge wb_clk)
if (bus_clr_wr) begin
bus_we_csr <= 1'b0;
bus_we_mem <= 1'b0;
end else begin
bus_we_csr <= ~wb_addr[9];
bus_we_mem <= wb_addr[9];
end
// Writes to CSR
always @(posedge wb_clk or posedge wb_rst)
if (wb_rst) begin
ctrl_scan_ena <= 1'b0;
ctrl_drv_rgbleden <= 1'b0;
ctrl_drv_curren <= 1'b0;
ctrl_trig_frame <= 4'h0;
ctrl_trig_ena <= 1'b0;
end else if (bus_we_csr) begin
ctrl_scan_ena <= wb_wdata[0];
ctrl_drv_rgbleden <= wb_wdata[1];
ctrl_drv_curren <= wb_wdata[2];
ctrl_trig_frame <= wb_wdata[27:24];
ctrl_trig_ena <= wb_wdata[30];
end
always @(posedge wb_clk)
ctrl_trig_clr <= bus_we_csr & wb_wdata[31];
// Writes to Frame Memory
assign fmw_addr = wb_addr[8:1];
assign fmw_ena = bus_we_mem;
assign fmw_data = {
2'b00, /* [47:46] n/a */
wb_wdata[22:16], /* [45:39] cyc_start[2] */
wb_wdata[30:24], /* [38:32] cyc_stop [2] */
wb_wdata[ 6: 0], /* [31:25] cyc_start[1] */
wb_wdata[14: 8], /* [24:18] cyc_stop [1] */
wb_wdata[22:16], /* [17:11] cyc_start[0] */
wb_wdata[30:24], /* [10: 4] cyc_stop [0] */
wb_wdata[ 3: 0] /* [ 3: 0] anode_sel */
};
assign fmw_mask = {
2'b11, /* [47:46] n/a */
{7{~wb_addr[0]}}, /* [45:39] cyc_start[2] */
{7{~wb_addr[0]}}, /* [38:32] cyc_stop [2] */
{7{~wb_addr[0]}}, /* [31:25] cyc_start[1] */
{7{~wb_addr[0]}}, /* [24:18] cyc_stop [1] */
{7{ wb_addr[0]}}, /* [17:11] cyc_start[0] */
{7{ wb_addr[0]}}, /* [10: 4] cyc_stop [0] */
{4{ wb_addr[0]}} /* [ 3: 0] anode_sel */
};
// Frame memory
// ------------
ice40_ebr #(
.READ_MODE (0),
.WRITE_MODE (0)
) frame_mem_I[2:0] (
.wr_addr (fmw_addr),
.wr_data (fmw_data),
.wr_mask (fmw_mask),
.wr_ena (fmw_ena),
.wr_clk (wb_clk),
.rd_addr (fmr_addr),
.rd_data (fmr_data),
.rd_ena (fmr_ena),
.rd_clk (led_clk)
);
// LED clock domain control
// ------------------------
// Enable
always @(posedge led_clk or posedge led_rst)
if (led_rst)
led_off_sync <= 5'b11111;
else
led_off_sync <= { led_off_sync[3:0], ~ctrl_scan_ena };
assign led_off_0 = led_off_sync[2];
assign led_off_1 = led_off_sync[3];
assign led_off_2 = led_off_sync[4];
// Trigger
xclk_pulse xclk_trig_upd_I (
.i_stb (bus_we_csr),
.i_clk (wb_clk),
.o_stb (trig_upd),
.o_clk (led_clk),
.rst (led_rst | wb_rst)
);
xclk_pulse xclk_trig_clr_I (
.i_stb (ctrl_trig_clr),
.i_clk (wb_clk),
.o_stb (trig_clr),
.o_clk (led_clk),
.rst (led_rst | wb_rst)
);
always @(posedge led_clk or posedge led_rst)
if (led_rst) begin
trig_frame <= 4'h0;
trig_ena <= 1'b0;
end else if (trig_upd) begin
trig_frame <= ctrl_trig_frame;
trig_ena <= ctrl_trig_ena;
end
assign trig_match = trig_ena & (trig_frame == frame_0) & frame_ce_0;
always @(posedge led_clk or posedge led_rst)
if (led_rst)
trig_out <= 1'b0;
else
trig_out <= (trig_out | trig_match) & ~trig_clr;
// Cycle counter
// -------------
// Counter
always @(posedge led_clk or posedge led_off_0)
if (led_off_0)
cycle_cnt_0 <= 8'h80;
else
cycle_cnt_0 <= tick_0 ? 8'h00 : (cycle_cnt_0 + 1);
// Cycle
assign cycle_0 = cycle_cnt_0[6:0];
always @(posedge led_clk)
cycle_1 <= cycle_0;
// Ticks
assign tick_0 = cycle_cnt_0[7] & ~led_off_0;
always @(posedge led_clk)
{ tick_2, tick_1 } <= { tick_1, tick_0 };
// Subframe counter
// ----------------
always @(posedge led_clk or posedge led_off_0)
if (led_off_0)
subframe_0 <= 4'h0;
else if (subframe_ce_0)
subframe_0 <= subframe_0 + 1;
assign subframe_last_0 = (subframe_0 == 4'hf);
assign subframe_ce_0 = tick_0;
// Play counter
// ------------
always @(posedge led_clk or posedge led_off_0)
if (led_off_0)
play_cnt_0 <= 0;
else if (play_ce_0)
play_cnt_0 <= play_last_0 ? 8'd94 : (play_cnt_0 - 1);
assign play_last_0 = play_cnt_0[7];
assign play_ce_0 = tick_0 & subframe_last_0;
// Frame counter
// -------------
always @(posedge led_clk or posedge led_off_0)
if (led_off_0)
frame_0 <= 0;
else if (frame_ce_0)
frame_0 <= frame_0 + 1;
assign frame_ce_0 = tick_0 & subframe_last_0 & play_last_0;
// Frame Memory Lookup
// -------------------
assign fmr_addr = { frame_0, subframe_0 };
assign fmr_ena = tick_0;
assign cyc_start_1[2] = fmr_data[45:39];
assign cyc_stop_1[2] = fmr_data[38:32];
assign cyc_start_1[1] = fmr_data[31:25];
assign cyc_stop_1[1] = fmr_data[24:18];
assign cyc_start_1[0] = fmr_data[17:11];
assign cyc_stop_1[0] = fmr_data[10: 4];
assign led_a_1 = fmr_data[3:0];
// PWM
// ---
genvar i;
generate
for (i=0; i<3; i=i+1)
begin
// Comparators (stop is offset !)
always @(*)
sig_start_1[i] = cyc_start_1[i] == cycle_1;
always @(posedge led_clk)
sig_stop_1[i] <= (cyc_stop_1[i] == cycle_1) & ~tick_1;
// State register
always @(posedge led_clk or posedge led_off_1)
begin
if (led_off_1) begin
started_2[i] <= 1'b0;
stopped_2[i] <= 1'b0;
end else begin
started_2[i] <= (started_2[i] | sig_start_1[i]) & ~tick_1;
stopped_2[i] <= (stopped_2[i] | sig_stop_1[i]) & ~tick_1;
end
end
// Final decision
always @(posedge led_clk or posedge led_off_2)
if (led_off_2)
led_c_3[i] <= 1'b0;
else
led_c_3[i] <= started_2[i] & ~stopped_2[i];
end
endgenerate
// Anode driver
// ------------
// Decode and gating
always @(posedge led_clk or posedge led_off_1)
if (led_off_1)
led_a_2 <= 0;
else begin
led_a_2 <= 0;
led_a_2[led_a_1] <= ~tick_1;
end
// IOBs
SB_IO #(
// .PIN_TYPE(6'b0101_01), // PIN_OUTPUT_REGISTERED, PIN_INPUT
.PIN_TYPE(6'b1101_01), // PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED, PIN_INPUT
.PULLUP(1'b0)
) led_anode_drv_I[13:0] (
.PACKAGE_PIN (led_a),
.OUTPUT_CLK (led_clk),
.OUTPUT_ENABLE (led_a_2),
.D_OUT_0 (1'b1)
);
// Cathode driver
// --------------
SB_RGBA_DRV #(
.CURRENT_MODE("0b1"),
.RGB0_CURRENT("0b000011"), /* Green : 4 mA */
.RGB1_CURRENT("0b000111"), /* Pink : 6 mA */
.RGB2_CURRENT("0b001111") /* Blue : 8 mA */
) led_cathode_drv_I (
.RGBLEDEN (ctrl_drv_rgbleden),
.RGB0PWM (led_c_3[0]),
.RGB1PWM (led_c_3[1]),
.RGB2PWM (led_c_3[2]),
.CURREN (ctrl_drv_curren),
.RGB0 (led_c[0]),
.RGB1 (led_c[1]),
.RGB2 (led_c[2])
);
endmodule // led_ctrl

2979
gateware/rtl/picorv32.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
/*
* picorv32_ice40_regs.v
*
* vim: ts=4 sw=4
*
* Implementation of register file for the PicoRV32 on iCE40
*
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module picorv32_ice40_regs (
input wire clk,
input wire wen,
input wire [5:0] waddr,
input wire [5:0] raddr1,
input wire [5:0] raddr2,
input wire [31:0] wdata,
output wire [31:0] rdata1,
output wire [31:0] rdata2
);
ice40_ebr #(
.READ_MODE(0),
.WRITE_MODE(0),
.MASK_WORKAROUND(0),
.NEG_WR_CLK(0),
.NEG_RD_CLK(1)
) regs[3:0] (
.wr_addr ({ 4{2'b00, waddr} }),
.wr_data ({ 2{wdata} }),
.wr_mask (64'h0000000000000000),
.wr_ena (wen),
.wr_clk (clk),
.rd_addr ({2'b00, raddr2, 2'b00, raddr2, 2'b00, raddr1, 2'b00, raddr1}),
.rd_data ({rdata2, rdata1}),
.rd_ena (1'b1),
.rd_clk (clk)
);
endmodule // picorv32_ice40_regs

278
gateware/rtl/pmu.v Normal file
View File

@ -0,0 +1,278 @@
/*
* pmu.v
*
* vim: ts=4 sw=4
*
* Platorm Management Unit
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module pmu (
// Wishbone
input wire [15:0] wb_addr,
output reg [31:0] wb_rdata,
input wire [31:0] wb_wdata,
input wire wb_we,
input wire wb_cyc,
output reg wb_ack,
// Buttons
input wire [ 1:0] btn,
// Power control
input wire pwr_usb_n,
input wire pwr_chg_n,
output wire pwr_off,
// Clock control
output wire sys_start,
output wire sys_stop,
output reg usb_ena,
// Wake up signal
input wire wakeup,
// Clock / Reset
input wire clk,
input wire rst
);
integer i;
// Signals
// -------
// Bus interface
wire bus_clr_rd;
wire bus_clr_wr;
wire bus_is_suspend;
reg [3:0] bus_ack_dly;
reg bus_we;
// Control signals
reg ctrl_sys_shutdown;
reg ctrl_sys_suspend;
reg ctrl_usb_off;
reg ctrl_usb_on;
// Buttons
wire btn_clk;
wire [1:0] btn_iob;
reg [2:0] btn_sync [0:1];
reg [4:0] btn_cnt [0:1];
reg [1:0] btn_val;
reg [1:0] btn_val_wb;
reg [16:0] btn_long_cnt [0:1];
reg [1:0] btn_long;
// Reboot
reg boot_wb_now;
reg [1:0] boot_wb_sel;
reg boot_now;
reg [1:0] boot_sel;
// Power/Clock Status/Control
wire iob_pwr_usb_n;
wire iob_pwr_chg_n;
reg stat_pwr_usb;
reg stat_pwr_chg;
wire pwr_off_trig;
reg pwr_off_i = 1'b0;
// Bus interface
// -------------
// Clears
assign bus_clr_rd = wb_ack | ~wb_cyc;
assign bus_clr_wr = wb_ack | ~wb_cyc | ~wb_we;
// Ack
always @(posedge clk)
wb_ack <= wb_cyc & (~bus_is_suspend | bus_ack_dly[3]) & ~wb_ack;
assign bus_is_suspend = wb_we & wb_wdata[6];
always @(posedge clk)
if (bus_clr_wr)
bus_ack_dly <= 0;
else
bus_ack_dly <= bus_ack_dly + 1;
// Reads
always @(posedge clk)
if (bus_clr_rd)
wb_rdata <= 32'h00000000;
else
wb_rdata <= {
27'd0,
usb_ena,
stat_pwr_usb,
stat_pwr_chg,
btn_val_wb[1],
btn_val_wb[0]
};
// Writes
always @(posedge clk)
if (bus_clr_wr) begin
bus_we <= 1'b0;
ctrl_sys_shutdown <= 1'b0;
ctrl_sys_suspend <= 1'b0;
ctrl_usb_off <= 1'b0;
ctrl_usb_on <= 1'b0;
end else begin
bus_we <= 1'b1;
ctrl_sys_shutdown <= wb_wdata[7];
ctrl_sys_suspend <= wb_wdata[6];
ctrl_usb_off <= wb_wdata[5];
ctrl_usb_on <= wb_wdata[4];
end
always @(posedge clk or posedge rst)
if (rst) begin
boot_wb_now <= 1'b0;
boot_wb_sel <= 2'b00;
end else if (bus_we) begin
boot_wb_now <= wb_wdata[2];
boot_wb_sel <= wb_wdata[1:0];
end
// Buttons
// -------
// Oscillator for button logic
(* ROUTE_THROUGH_FABRIC=1 *)
SB_LFOSC btn_osc (
.CLKLFPU (1'b1),
.CLKLFEN (1'b1),
.CLKLF (btn_clk)
);
// IOBs
SB_IO #(
.PIN_TYPE(6'b000001), // PIN_OUTPUT_NONE, PIN_INPUT
.PULLUP(1'b1),
.IO_STANDARD("SB_LVCMOS")
) btn_iob_I[1:0] (
.PACKAGE_PIN (btn),
.D_IN_0 (btn_iob)
);
// Synchronizer
initial
for (i=0; i<2; i=i+1)
btn_sync[i] = 1;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_sync[i] <= { btn_sync[i][1:0], btn_iob[i] };
// Small counter for debounce
initial
for (i=0; i<2; i=i+1)
btn_cnt[i] = 0;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_cnt[i] <= btn_sync[i][2] ? (btn_cnt[i] + {4'h0, btn_cnt[i][4]}) : 5'h10;
always @(*)
for (i=0; i<2; i=i+1)
btn_val[i] = btn_cnt[i][4];
// Synchronize value in sys clock domain
always @(posedge clk)
for (i=0; i<2; i=i+1)
btn_val_wb[i] <= btn_val[i];
// Long counter to detect very long presses
// ( ~6.5 sec )
initial
for (i=0; i<2; i=i+1)
btn_long_cnt[i] = 0;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_long_cnt[i] <= (btn_long_cnt[i] + btn_val[i]) & {17{btn_val[i]}};
always @(*)
for (i=0; i<2; i=i+1)
btn_long[i] = btn_long_cnt[i][16];
// Reboot
// ------
// Command from wb or button force
always @(*)
begin
if (btn_long[0]) begin
boot_now = 1'b1;
boot_sel = 2'b01;
end else begin
boot_now = boot_wb_now;
boot_sel = boot_wb_sel;
end
end
// Core
SB_WARMBOOT warmboot (
.BOOT (boot_now),
.S0 (boot_sel[0]),
.S1 (boot_sel[1])
);
// Power / Clock control
// ---------------------
// Synchronizers for status
SB_IO #(
.PIN_TYPE(6'b000000), // PIN_OUTPUT_NONE, PIN_INPUT_REGISTERED
.PULLUP(1'b1),
.IO_STANDARD("SB_LVCMOS")
) stat_iob_I[1:0] (
.PACKAGE_PIN ({ pwr_usb_n, pwr_chg_n}),
.INPUT_CLK (clk),
.D_IN_0 ({iob_pwr_usb_n, iob_pwr_chg_n})
);
always @(posedge clk)
begin
stat_pwr_usb <= ~iob_pwr_usb_n;
stat_pwr_chg <= ~iob_pwr_chg_n;
end
// Enable system clock on wakeup
assign sys_start = wakeup;
// Disable system clock on requests
assign sys_stop = ctrl_sys_suspend;
// Power-off on long press and on requests
assign pwr_off_trig = btn_long[1] | ctrl_sys_shutdown;
always @(posedge pwr_off_trig or posedge rst)
if (rst)
pwr_off_i <= 1'b0;
else
pwr_off_i <= 1'b1;
assign pwr_off = pwr_off_i;
// USB ena
always @(posedge clk)
if (rst)
usb_ena <= 1'b0;
else
usb_ena <= (usb_ena | ctrl_usb_on) & ~ctrl_usb_off;
endmodule // pmu

40
gateware/rtl/soc_bram.v Normal file
View File

@ -0,0 +1,40 @@
/*
* soc_bram.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module soc_bram #(
parameter integer SIZE = 256,
parameter integer AW = $clog2(SIZE),
parameter INIT_FILE = ""
)(
input wire [AW-1:0] addr,
output reg [31:0] rdata,
input wire [31:0] wdata,
input wire [ 3:0] wmsk,
input wire we,
input wire clk
);
(* no_rw_check *)
reg [31:0] mem [0:SIZE-1];
initial
if (INIT_FILE != "")
$readmemh(INIT_FILE, mem);
always @(posedge clk) begin
rdata <= mem[addr];
if (we & ~wmsk[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
if (we & ~wmsk[1]) mem[addr][15: 8] <= wdata[15: 8];
if (we & ~wmsk[2]) mem[addr][23:16] <= wdata[23:16];
if (we & ~wmsk[3]) mem[addr][31:24] <= wdata[31:24];
end
endmodule // soc_bram

View File

@ -0,0 +1,163 @@
/*
* soc_picorv32_base.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module soc_picorv32_base #(
parameter integer WB_N = 6,
parameter integer WB_DW = 32,
parameter integer WB_AW = 16,
parameter integer BRAM_AW = 8, /* Default 1k */
parameter integer SPRAM_AW = 14, /* 0 => none, 14 => 64k, 15 => 128k */
/* auto */
parameter integer WB_MW = WB_DW / 8,
parameter integer WB_RW = WB_DW * WB_N,
parameter integer WB_AI = $clog2(WB_MW)
)(
// Wishbone
output wire [WB_AW-1:0] wb_addr,
input wire [WB_RW-1:0] wb_rdata,
output wire [WB_DW-1:0] wb_wdata,
output wire [WB_MW-1:0] wb_wmsk,
output wire wb_we,
output wire [WB_N -1:0] wb_cyc,
input wire [WB_N -1:0] wb_ack,
// Clock / Reset
input wire clk,
input wire rst
);
// Signals
// -------
// Memory bus
wire mem_valid;
wire mem_instr;
wire mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_rdata;
wire [31:0] mem_wdata;
wire [ 3:0] mem_wstrb;
// RAM
// BRAM
wire [14:0] bram_addr;
wire [31:0] bram_rdata;
wire [31:0] bram_wdata;
wire [ 3:0] bram_wmsk;
wire bram_we;
// SPRAM
wire [14:0] spram_addr;
wire [31:0] spram_rdata;
wire [31:0] spram_wdata;
wire [ 3:0] spram_wmsk;
wire spram_we;
// CPU
// ---
picorv32 #(
.PROGADDR_RESET(32'h 0000_0000),
.STACKADDR(4 << BRAM_AW),
.BARREL_SHIFTER(1),
.COMPRESSED_ISA(0),
.ENABLE_COUNTERS(0),
.ENABLE_FAST_MUL(1),
.ENABLE_MUL(0),
.ENABLE_DIV(0),
.ENABLE_IRQ(0),
.ENABLE_IRQ_QREGS(0),
.CATCH_MISALIGN(0),
.CATCH_ILLINSN(0)
) cpu_I (
.clk (clk),
.resetn (~rst),
.mem_valid (mem_valid),
.mem_instr (mem_instr),
.mem_ready (mem_ready),
.mem_addr (mem_addr),
.mem_wdata (mem_wdata),
.mem_wstrb (mem_wstrb),
.mem_rdata (mem_rdata)
);
// Bus interface
// -------------
soc_picorv32_bridge #(
.WB_N (WB_N),
.WB_DW(WB_DW),
.WB_AW(WB_AW),
.WB_AI(WB_AI)
) pb_I (
.pb_addr (mem_addr),
.pb_rdata (mem_rdata),
.pb_wdata (mem_wdata),
.pb_wstrb (mem_wstrb),
.pb_valid (mem_valid),
.pb_ready (mem_ready),
.bram_addr (bram_addr),
.bram_rdata (bram_rdata),
.bram_wdata (bram_wdata),
.bram_wmsk (bram_wmsk),
.bram_we (bram_we),
.spram_addr (spram_addr),
.spram_rdata (spram_rdata),
.spram_wdata (spram_wdata),
.spram_wmsk (spram_wmsk),
.spram_we (spram_we),
.wb_addr (wb_addr),
.wb_wdata (wb_wdata),
.wb_wmsk (wb_wmsk),
.wb_rdata (wb_rdata),
.wb_cyc (wb_cyc),
.wb_we (wb_we),
.wb_ack (wb_ack),
.clk (clk),
.rst (rst)
);
// Local memory
// ------------
// BRAM memory
soc_bram #(
.SIZE(1 << BRAM_AW),
.INIT_FILE("boot.hex")
) bram_I (
.addr (bram_addr[BRAM_AW-1:0]),
.rdata (bram_rdata),
.wdata (bram_wdata),
.wmsk (bram_wmsk),
.we (bram_we),
.clk (clk)
);
// SPRAM memory
generate
if (SPRAM_AW > 0)
soc_spram #(
.AW(SPRAM_AW)
) spram_I (
.addr (spram_addr[SPRAM_AW-1:0]),
.rdata (spram_rdata),
.wdata (spram_wdata),
.wmsk (spram_wmsk),
.we (spram_we),
.clk (clk)
);
endgenerate
endmodule // soc_picorv32_base

View File

@ -0,0 +1,186 @@
/*
* soc_picorv32_bridge.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module soc_picorv32_bridge #(
parameter integer WB_N = 8,
parameter integer WB_DW = 32,
parameter integer WB_AW = 16,
parameter integer WB_AI = 2,
parameter integer WB_REG = 0 // [0] = cyc / [1] = addr/wdata/wstrb / [2] = ack/rdata
)(
/* PicoRV32 bus */
input wire [31:0] pb_addr,
output wire [31:0] pb_rdata,
input wire [31:0] pb_wdata,
input wire [ 3:0] pb_wstrb,
input wire pb_valid,
output wire pb_ready,
/* BRAM */
output wire [14:0] bram_addr,
input wire [31:0] bram_rdata,
output wire [31:0] bram_wdata,
output wire [ 3:0] bram_wmsk,
output wire bram_we,
/* SPRAM */
output wire [14:0] spram_addr,
input wire [31:0] spram_rdata,
output wire [31:0] spram_wdata,
output wire [ 3:0] spram_wmsk,
output wire spram_we,
/* Wishbone buses */
output wire [WB_AW-1:0] wb_addr,
input wire [(WB_DW*WB_N)-1:0] wb_rdata,
output wire [WB_DW-1:0] wb_wdata,
output wire [(WB_DW/8)-1:0] wb_wmsk,
output wire wb_we,
output wire [WB_N-1:0] wb_cyc,
input wire [WB_N-1:0] wb_ack,
/* Clock / Reset */
input wire clk,
input wire rst
);
// Signals
// -------
wire ram_sel;
reg ram_rdy;
wire [31:0] ram_rdata;
(* keep *) wire [WB_N-1:0] wb_match;
(* keep *) wire wb_cyc_rst;
reg [31:0] wb_rdata_or;
wire [31:0] wb_rdata_out;
wire wb_rdy;
// RAM access
// ----------
// BRAM : 0x00000000 -> 0x0001ffff
// SPRAM : 0x00020000 -> 0x0003ffff
assign bram_addr = pb_addr[16:2];
assign spram_addr = pb_addr[16:2];
assign bram_wdata = pb_wdata;
assign spram_wdata = pb_wdata;
assign bram_wmsk = ~pb_wstrb;
assign spram_wmsk = ~pb_wstrb;
assign bram_we = pb_valid & ~pb_addr[31] & |pb_wstrb & ~pb_addr[17];
assign spram_we = pb_valid & ~pb_addr[31] & |pb_wstrb & pb_addr[17];
assign ram_rdata = ~pb_addr[31] ? (pb_addr[17] ? spram_rdata : bram_rdata) : 32'h00000000;
assign ram_sel = pb_valid & ~pb_addr[31];
always @(posedge clk)
ram_rdy <= ram_sel && ~ram_rdy;
// Wishbone
// --------
// wb[x] = 0x8x000000 - 0x8xffffff
// Access Cycle
genvar i;
for (i=0; i<WB_N; i=i+1)
assign wb_match[i] = (pb_addr[27:24] == i);
if (WB_REG & 1) begin
// Register
reg [WB_N-1:0] wb_cyc_reg;
always @(posedge clk)
if (wb_cyc_rst)
wb_cyc_reg <= 0;
else
wb_cyc_reg <= wb_match & ~wb_ack;
assign wb_cyc = wb_cyc_reg;
end else begin
// Direct connection
assign wb_cyc = wb_cyc_rst ? { WB_N{1'b0} } : wb_match;
end
// Addr / Write-Data / Write-Mask / Write-Enable
if (WB_REG & 2) begin
// Register
reg [WB_AW-1:0] wb_addr_reg;
reg [WB_DW-1:0] wb_wdata_reg;
reg [(WB_DW/8)-1:0] wb_wmsk_reg;
reg wb_we_reg;
always @(posedge clk)
begin
wb_addr_reg <= pb_addr[WB_AW+WB_AI-1:WB_AI];
wb_wdata_reg <= pb_wdata[WB_DW-1:0];
wb_wmsk_reg <= ~pb_wstrb[(WB_DW/8)-1:0];
wb_we_reg <= |pb_wstrb;
end
assign wb_addr = wb_addr_reg;
assign wb_wdata = wb_wdata_reg;
assign wb_wmsk = wb_wmsk_reg;
assign wb_we = wb_we_reg;
end else begin
// Direct connection
assign wb_addr = pb_addr[WB_AW+WB_AI-1:WB_AI];
assign wb_wdata = pb_wdata[WB_DW-1:0];
assign wb_wmsk = pb_wstrb[(WB_DW/8)-1:0];
assign wb_we = |pb_wstrb;
end
// Ack / Read-Data
always @(*)
begin : wb_or
integer i;
wb_rdata_or = 0;
for (i=0; i<WB_N; i=i+1)
wb_rdata_or[WB_DW-1:0] = wb_rdata_or[WB_DW-1:0] | wb_rdata[WB_DW*i+:WB_DW];
end
if (WB_REG & 4) begin
// Register
reg wb_rdy_reg;
reg [31:0] wb_rdata_reg;
always @(posedge clk)
wb_rdy_reg <= |wb_ack;
always @(posedge clk)
if (wb_cyc_rst)
wb_rdata_reg <= 32'h00000000;
else
wb_rdata_reg <= wb_rdata_or;
assign wb_cyc_rst = ~pb_valid | ~pb_addr[31] | wb_rdy_reg;
assign wb_rdy = wb_rdy_reg;
assign wb_rdata_out = wb_rdata_reg;
end else begin
// Direct connection
assign wb_cyc_rst = ~pb_valid | ~pb_addr[31];
assign wb_rdy = |wb_ack;
assign wb_rdata_out = wb_rdata_or;
end
// Final data combining
// --------------------
assign pb_rdata = ram_rdata | wb_rdata_out;
assign pb_ready = ram_rdy | wb_rdy;
endmodule // soc_picorv32_bridge

43
gateware/rtl/soc_spram.v Normal file
View File

@ -0,0 +1,43 @@
/*
* soc_spram.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module soc_spram #(
parameter integer AW = 14
)(
input wire [AW-1:0] addr,
output wire [31:0] rdata,
input wire [31:0] wdata,
input wire [ 3:0] wmsk,
input wire we,
input wire clk
);
wire [7:0] msk_nibble = {
wmsk[3], wmsk[3],
wmsk[2], wmsk[2],
wmsk[1], wmsk[1],
wmsk[0], wmsk[0]
};
ice40_spram_gen #(
.ADDR_WIDTH(AW),
.DATA_WIDTH(32)
) spram_I (
.addr(addr),
.rd_data(rdata),
.rd_ena(1'b1),
.wr_data(wdata),
.wr_mask(msk_nibble),
.wr_ena(we),
.clk(clk)
);
endmodule // soc_spram

View File

@ -0,0 +1,60 @@
/*
* soc_usb_buf_bridge.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module soc_usb_buf_bridge (
// Wishbone (from SoC)
input wire [15:0] wb_addr,
output reg [31:0] wb_rdata,
input wire [31:0] wb_wdata,
input wire [ 3:0] wb_wmsk,
input wire wb_we,
input wire wb_cyc,
output reg wb_ack,
// USB EP buffer
output wire [ 8:0] ep_tx_addr_0,
output wire [31:0] ep_tx_data_0,
output wire ep_tx_we_0,
output wire [ 8:0] ep_rx_addr_0,
input wire [31:0] ep_rx_data_1,
output wire ep_rx_re_0,
// Clock / Reset
input wire clk,
input wire rst
);
// Ack
always @(posedge clk)
wb_ack <= wb_cyc & ~wb_ack;
// Address
assign ep_tx_addr_0 = wb_addr[8:0];
assign ep_rx_addr_0 = wb_addr[8:0];
// Read Enable
assign ep_rx_re_0 = wb_cyc;
// Read
always @(*)
if (~wb_ack)
wb_rdata = 32'h00000000;
else
wb_rdata = ep_rx_data_1;
// Write data
assign ep_tx_data_0 = wb_wdata;
// Write enable
assign ep_tx_we_0 = wb_ack & wb_we;
endmodule // soc_usb_buf_bridge

209
gateware/rtl/sysmgr_1.v Normal file
View File

@ -0,0 +1,209 @@
/*
* sysmgr.v
*
* vim: ts=4 sw=4
*
* System Clock / Reset generation
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module sysmgr (
// Control
input wire sys_start,
input wire sys_stop,
input wire usb_ena,
// LED clock ( 6 MHz )
output wire clk_led,
output wire rst_led,
// System clock ( 24 MHz )
output wire clk_sys,
output wire rst_sys,
// USB ( 48 MHz )
output wire clk_usb,
output wire rst_usb
);
// Signals
// -------
wire clk_osc;
wire clk_base;
wire pll_lock;
reg [3:0] clk_div;
reg clk_usb_i;
reg clk_sys_i;
wire clk_led_i;
reg [3:0] rst_cnt;
wire rst_i;
wire rst_gbuf;
reg [2:0] sys_start_s;
reg [2:0] sys_stop_s;
wire usb_rst_trig;
reg [3:0] usb_rst_cnt;
wire usb_rst_i;
reg [3:0] usb_off_dly;
reg sys_off;
wire usb_off;
// SB_HFOSC
// -------
// Generates 12 MHz
(* ROUTE_THROUGH_FABRIC = 1 *)
SB_HFOSC #(
.CLKHF_DIV("0b10")
) osc_I (
.CLKHFPU(1'b1),
.CLKHFEN(1'b1),
.CLKHF(clk_osc)
);
// PLL
// ---
SB_PLL40_CORE #(
.DIVR (4'b0000),
.DIVF (7'b0111111),
.DIVQ (3'b011),
.FILTER_RANGE (3'b001),
.FEEDBACK_PATH ("SIMPLE"),
.DELAY_ADJUSTMENT_MODE_FEEDBACK ("FIXED"),
.FDA_FEEDBACK (4'b0000),
.SHIFTREG_DIV_MODE (2'b00),
.PLLOUT_SELECT ("GENCLK"),
.ENABLE_ICEGATE (1'b0)
) pll_I (
.REFERENCECLK (clk_osc),
.PLLOUTCORE (clk_base),
.PLLOUTGLOBAL (),
.EXTFEEDBACK (1'b0),
.DYNAMICDELAY (8'h00),
.RESETB (1'b1),
.BYPASS (1'b0),
.LATCHINPUTVALUE (1'b0),
.LOCK (pll_lock),
.SDI (1'b0),
.SDO (),
.SCLK (1'b0)
);
// Dividers
// --------
// Counter
always @(posedge clk_base)
clk_div <= clk_div + 1;
// USB is div-by-2 + gated
always @(posedge clk_base)
clk_usb_i <= clk_div[0] & ~usb_off;
// SYS is div-by-4 + gated
always @(posedge clk_base)
clk_sys_i <= clk_div[1] & ~sys_off;
// LED is div-by-16
assign clk_led_i = clk_div[3];
// Global buffers
SB_GB clk_usb_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_usb_i),
.GLOBAL_BUFFER_OUTPUT(clk_usb)
);
SB_GB clk_sys_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_sys_i),
.GLOBAL_BUFFER_OUTPUT(clk_sys)
);
SB_GB clk_led_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_led_i),
.GLOBAL_BUFFER_OUTPUT(clk_led)
);
// USB Enable
// ----------
// Delay falling latch signal
always @(posedge clk_base)
usb_off_dly <= usb_ena ? 4'b0000 : { usb_off_dly[2:0], 1'b1 };
assign usb_off = usb_off_dly[3];
// USB Reset
// ---------
// Reset trigger
assign usb_rst_trig = ~usb_ena | ~pll_lock;
// Reset counter
always @(posedge clk_usb or posedge usb_rst_trig)
if (usb_rst_trig)
usb_rst_cnt <= 4'h8;
else
usb_rst_cnt <= usb_rst_cnt + usb_rst_i;
assign usb_rst_i = usb_rst_cnt[3];
// Global buffer
SB_GB rst_usb_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(usb_rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_usb)
);
// SYS Enable
// ----------
// Synch triggers
always @(posedge clk_base)
begin
sys_start_s <= { ~sys_start_s[1] & sys_start_s[0], sys_start_s[0], sys_start };
sys_stop_s <= { ~sys_stop_s[1] & sys_stop_s[0], sys_stop_s[0], sys_stop };
end
always @(posedge clk_base)
sys_off <= (sys_off & ~rst_i & ~sys_start_s[2]) | sys_stop_s[2];
// Reset LED / SYS
// ---------------
// Counter
always @(posedge clk_led or negedge pll_lock)
if (~pll_lock)
rst_cnt <= 4'h8;
else
rst_cnt <= rst_cnt + rst_i;
assign rst_i = rst_cnt[3];
// Global buffer
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_gbuf)
);
// Distribution
assign rst_led = rst_gbuf;
assign rst_sys = rst_gbuf;
endmodule // sysmgr

203
gateware/rtl/sysmgr_2.v Normal file
View File

@ -0,0 +1,203 @@
/*
* sysmgr.v
*
* vim: ts=4 sw=4
*
* System Clock / Reset generation
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module sysmgr (
// Control
input wire sys_start,
input wire sys_stop,
input wire usb_ena,
// LED clock ( 6 MHz )
output wire clk_led,
output wire rst_led,
// System clock ( 24 MHz )
output wire clk_sys,
output wire rst_sys,
// USB ( 48 MHz )
output wire clk_usb,
output wire rst_usb
);
// Signals
// -------
wire clk_osc;
wire clk_base;
wire pll_lock;
reg [2:0] clk_div;
reg clk_sys_i;
wire clk_led_i;
reg [3:0] rst_cnt;
wire rst_i;
wire rst_gbuf;
reg [2:0] sys_start_s;
reg [2:0] sys_stop_s;
wire usb_rst_trig;
reg [3:0] usb_rst_cnt;
wire usb_rst_i;
reg [3:0] usb_off_dly;
wire sys_off;
wire usb_off;
// SB_HFOSC
// -------
// Generates 12 MHz
(* ROUTE_THROUGH_FABRIC = 1 *)
SB_HFOSC #(
.CLKHF_DIV("0b10")
) osc_I (
.CLKHFPU(1'b1),
.CLKHFEN(1'b1),
.CLKHF(clk_osc)
);
// PLL
// ---
SB_PLL40_2F_CORE #(
.DIVR (4'b0000),
.DIVF (7'b0111111),
.DIVQ (3'b100),
.FILTER_RANGE (3'b001),
.FEEDBACK_PATH ("SIMPLE"),
.DELAY_ADJUSTMENT_MODE_FEEDBACK ("FIXED"),
.FDA_FEEDBACK (4'b0000),
.SHIFTREG_DIV_MODE (2'b00),
.PLLOUT_SELECT_PORTA ("GENCLK"),
.PLLOUT_SELECT_PORTB ("GENCLK"),
.ENABLE_ICEGATE_PORTA (1'b1),
.ENABLE_ICEGATE_PORTB (1'b0),
) pll_I (
.REFERENCECLK (clk_osc),
.PLLOUTCOREB (),
.PLLOUTGLOBALB (clk_usb),
.PLLOUTCOREA (clk_base),
.PLLOUTGLOBALA (),
.EXTFEEDBACK (1'b0),
.DYNAMICDELAY (8'h00),
.RESETB (1'b1),
.BYPASS (1'b0),
.LATCHINPUTVALUE (usb_off),
.LOCK (pll_lock),
.SDI (1'b0),
.SDO (),
.SCLK (1'b0)
);
// Dividers
// --------
// Counter
always @(posedge clk_base)
clk_div <= clk_div + 1;
// SYS is div-by-2 + gated
always @(posedge clk_base)
clk_sys_i <= clk_div[0] & ~sys_off;
// LED is div-by-8
assign clk_led_i = clk_div[2];
// Global buffers
SB_GB clk_sys_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_sys_i),
.GLOBAL_BUFFER_OUTPUT(clk_sys)
);
SB_GB clk_led_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_led_i),
.GLOBAL_BUFFER_OUTPUT(clk_led)
);
// USB Enable
// ----------
// Delay falling latch signal
always @(posedge clk_base)
usb_off_dly <= usb_ena ? 4'b0000 : { usb_off_dly[2:0], 1'b1 };
assign usb_off = usb_off_dly[3];
// USB Reset
// ---------
// Reset trigger
assign usb_rst_trig = ~usb_ena | ~pll_lock;
// Reset counter
always @(posedge clk_usb or posedge usb_rst_trig)
if (usb_rst_trig)
usb_rst_cnt <= 4'h8;
else
usb_rst_cnt <= usb_rst_cnt + usb_rst_i;
assign usb_rst_i = usb_rst_cnt[3];
// Global buffer
SB_GB rst_usb_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(usb_rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_usb)
);
// SYS Enable
// ----------
// Synch triggers
always @(posedge clk_base)
begin
sys_start_s <= { ~sys_start_s[1] & sys_start_s[0], sys_start_s[0], sys_start };
sys_stop_s <= { ~sys_stop_s[1] & sys_stop_s[0], sys_stop_s[0], sys_stop };
end
always @(posedge clk_base)
sys_off <= (sys_off & ~rst_i & ~sys_start_s[2]) | sys_stop_s[2];
// Reset LED / SYS
// ---------------
// Counter
always @(posedge clk_led or negedge pll_lock)
if (~pll_lock)
rst_cnt <= 4'h8;
else
rst_cnt <= rst_cnt + rst_i;
assign rst_i = rst_cnt[3];
// Global buffer
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_gbuf)
);
// Distribution
assign rst_led = rst_gbuf;
assign rst_sys = rst_gbuf;
endmodule // sysmgr

197
gateware/rtl/sysmgr_3.v Normal file
View File

@ -0,0 +1,197 @@
/*
* sysmgr.v
*
* vim: ts=4 sw=4
*
* System Clock / Reset generation
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module sysmgr (
// Control
input wire sys_start,
input wire sys_stop,
input wire usb_ena,
// LED clock ( 6 MHz )
output wire clk_led,
output wire rst_led,
// System clock ( 24 MHz )
output wire clk_sys,
output wire rst_sys,
// USB ( 48 MHz )
output wire clk_usb,
output wire rst_usb
);
// Signals
// -------
wire clk_base;
wire pll_lock;
reg [2:0] clk_div;
reg clk_sys_i;
wire clk_led_i;
reg [7:0] rst_cnt = 8'h00;
reg rst_i;
wire rst_gbuf;
reg [2:0] sys_start_s;
reg [2:0] sys_stop_s;
wire usb_rst_trig;
reg [3:0] usb_rst_cnt;
wire usb_rst_i;
reg [3:0] usb_ena_dly;
wire usb_ena_i;
reg sys_off;
// SB_HFOSC
// -------
// Generates 48 MHz
(* ROUTE_THROUGH_FABRIC = 1 *)
SB_HFOSC #(
.CLKHF_DIV("0b00")
) osc_I (
.CLKHFPU(1'b1),
.CLKHFEN(1'b1),
.CLKHF(clk_base)
);
// PLL
// ---
SB_PLL40_CORE #(
.DIVR (4'b0000),
.DIVF (7'b0111111),
.DIVQ (3'b100),
.FILTER_RANGE (3'b001),
.FEEDBACK_PATH ("SIMPLE"),
.DELAY_ADJUSTMENT_MODE_FEEDBACK ("FIXED"),
.FDA_FEEDBACK (4'b0000),
.SHIFTREG_DIV_MODE (2'b00),
.PLLOUT_SELECT ("GENCLK"),
.ENABLE_ICEGATE (1'b0)
) pll_I (
.REFERENCECLK (clk_div[1]),
.PLLOUTCORE (),
.PLLOUTGLOBAL (clk_usb),
.EXTFEEDBACK (1'b0),
.DYNAMICDELAY (8'h00),
.RESETB (usb_ena_i),
.BYPASS (1'b0),
.LATCHINPUTVALUE (1'b0),
.LOCK (pll_lock),
.SDI (1'b0),
.SDO (),
.SCLK (1'b0)
);
// Dividers
// --------
// Counter
always @(posedge clk_base)
clk_div <= clk_div + 1;
// SYS is div-by-2 + gated
always @(posedge clk_base)
clk_sys_i <= clk_div[0] & ~sys_off;
// LED is div-by-8
assign clk_led_i = clk_div[2];
// Global buffers
SB_GB clk_sys_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_sys_i),
.GLOBAL_BUFFER_OUTPUT(clk_sys)
);
SB_GB clk_led_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_led_i),
.GLOBAL_BUFFER_OUTPUT(clk_led)
);
// USB Enable
// ----------
// Delay falling latch signal
always @(posedge clk_base)
usb_ena_dly <= usb_ena ? 4'b1111 : { usb_ena_dly[2:0], 1'b0 };
assign usb_ena_i = usb_ena_dly[3];
// USB Reset
// ---------
// Reset trigger
assign usb_rst_trig = ~usb_ena | ~pll_lock;
// Reset counter
always @(posedge clk_usb or posedge usb_rst_trig)
if (usb_rst_trig)
usb_rst_cnt <= 4'h8;
else
usb_rst_cnt <= usb_rst_cnt + usb_rst_i;
assign usb_rst_i = usb_rst_cnt[3];
// Global buffer
SB_GB rst_usb_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(usb_rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_usb)
);
// SYS Enable
// ----------
// Synch triggers
always @(posedge clk_base)
begin
sys_start_s <= { ~sys_start_s[1] & sys_start_s[0], sys_start_s[0], sys_start };
sys_stop_s <= { ~sys_stop_s[1] & sys_stop_s[0], sys_stop_s[0], sys_stop };
end
always @(posedge clk_base)
sys_off <= (sys_off & ~rst_i & ~sys_start_s[2]) | sys_stop_s[2];
// Reset LED / SYS
// ---------------
// Counter
always @(posedge clk_led)
rst_cnt <= rst_cnt + rst_i;
always @(posedge clk_led)
rst_i <= (rst_cnt[7:4] != 4'hf);
// Global buffer
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_gbuf)
);
// Distribution
assign rst_led = rst_gbuf;
assign rst_sys = rst_gbuf;
endmodule // sysmgr

332
gateware/rtl/top.v Normal file
View File

@ -0,0 +1,332 @@
/*
* top.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module top (
// SPI
inout wire [3:0] spi_io,
output wire spi_clk,
output wire spi_cs_n,
// USB
inout wire usb_dp,
inout wire usb_dn,
output wire usb_pu,
// Power
input wire pwr_usb_n,
input wire pwr_chg_n,
output wire pwr_off,
// Buttons
input wire [1:0] btn,
// I2C
inout wire scl,
inout wire sda,
// Speaker
output wire hp_p,
output wire hp_n,
// LED matrix
output wire [13:0] led_a,
output wire [2:0] led_c
);
localparam integer WN = 6;
genvar i;
// Signals
// -------
// Wishbone
wire [15:0] wb_addr;
wire [31:0] wb_rdata [0:WN-1];
wire [31:0] wb_wdata;
wire [3:0] wb_wmsk;
wire [WN-1:0] wb_cyc;
wire wb_we;
wire [WN-1:0] wb_ack;
wire [(32*WN)-1:0] wb_rdata_flat;
// I2C
wire i2c_scl_oe;
wire i2c_scl_i;
wire i2c_sda_oe;
wire i2c_sda_i;
// USB Core
// Wishbone in 48 MHz domain
wire [11:0] ub_addr;
wire [15:0] ub_wdata;
wire [15:0] ub_rdata;
wire ub_cyc;
wire ub_we;
wire ub_ack;
// EP Buffer
wire [ 8:0] ep_tx_addr_0;
wire [31:0] ep_tx_data_0;
wire ep_tx_we_0;
wire [ 8:0] ep_rx_addr_0;
wire [31:0] ep_rx_data_1;
wire ep_rx_re_0;
// Clock Control
wire wakeup;
wire sys_start;
wire sys_stop;
wire usb_ena;
// Clock / Reset
wire clk_led;
wire rst_led;
wire clk_sys;
wire rst_sys;
wire clk_usb;
wire rst_usb;
// SoC
// ---
soc_picorv32_base #(
.WB_N (WN),
.WB_DW (32),
.WB_AW (16),
.BRAM_AW (8), // 1k BRAM
.SPRAM_AW (14) // 64k SPRAM
) base_I (
.wb_addr (wb_addr),
.wb_rdata (wb_rdata_flat),
.wb_wdata (wb_wdata),
.wb_wmsk (wb_wmsk),
.wb_we (wb_we),
.wb_cyc (wb_cyc),
.wb_ack (wb_ack),
.clk (clk_sys),
.rst (rst_sys)
);
for (i=0; i<WN; i=i+1)
assign wb_rdata_flat[i*32+:32] = wb_rdata[i];
// PMU [0]
// ---
pmu pmu_I (
.wb_addr (wb_addr[15:0]),
.wb_rdata (wb_rdata[0]),
.wb_wdata (wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc[0]),
.wb_ack (wb_ack[0]),
.btn (btn),
.pwr_usb_n (pwr_usb_n),
.pwr_chg_n (pwr_chg_n),
.pwr_off (pwr_off),
.sys_start (sys_start),
.sys_stop (sys_stop),
.usb_ena (usb_ena),
.wakeup (wakeup),
.clk (clk_sys),
.rst (rst_sys)
);
// SPI [1]
// ---
ice40_spi_wb #(
.N_CS(1),
.WITH_IOB(1),
.UNIT(0)
) spi_I (
.pad_mosi (spi_io[0]),
.pad_miso (spi_io[1]),
.pad_clk (spi_clk),
.pad_csn (spi_cs_n),
.wb_addr (wb_addr[3:0]),
.wb_rdata (wb_rdata[1]),
.wb_wdata (wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc[1]),
.wb_ack (wb_ack[1]),
.clk (clk_sys),
.rst (rst_sys)
);
assign spi_io[3:2] = 4'bzz;
// I2C [2]
// ---
// Controller
i2c_master_wb #(
.DW(4),
.TW(15),
.CLOCK_STRETCH(1),
.FIFO_DEPTH(0)
) i2c_I (
.scl_oe (i2c_scl_oe),
.scl_i (i2c_scl_i),
.sda_oe (i2c_sda_oe),
.sda_i (i2c_sda_i),
.wb_rdata(wb_rdata[2]),
.wb_wdata(wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc[2]),
.wb_ack (wb_ack[2]),
.clk (clk_sys),
.rst (rst_sys)
);
// IOBs
SB_IO #(
.PIN_TYPE(6'b110100),
.PULLUP(1'b1),
.IO_STANDARD("SB_LVCMOS")
) i2c_iob_I[1:0] (
.PACKAGE_PIN ({scl, sda}),
.INPUT_CLK (clk_sys),
.OUTPUT_CLK (clk_sys),
.OUTPUT_ENABLE({i2c_scl_oe, i2c_sda_oe}),
.D_OUT_0 (1'b0),
.D_IN_0 ({i2c_scl_i, i2c_sda_i})
);
// USB Buffer [3]
// ----------
soc_usb_buf_bridge usb_buf_I (
.wb_addr (wb_addr),
.wb_rdata (wb_rdata[3]),
.wb_wdata (wb_wdata),
.wb_wmsk (wb_wmsk),
.wb_we (wb_we),
.wb_cyc (wb_cyc[3]),
.wb_ack (wb_ack[3]),
.ep_tx_addr_0 (ep_tx_addr_0),
.ep_tx_data_0 (ep_tx_data_0),
.ep_tx_we_0 (ep_tx_we_0),
.ep_rx_addr_0 (ep_rx_addr_0),
.ep_rx_data_1 (ep_rx_data_1),
.ep_rx_re_0 (ep_rx_re_0),
.clk (clk_sys),
.rst (rst_sys)
);
// USB core [4]
// --------
// Cross-clock
xclk_wb #(
.DW(16),
.AW(12)
) wb_48m_xclk_I (
.s_addr (wb_addr[11:0]),
.s_wdata (wb_wdata[15:0]),
.s_rdata (wb_rdata[4][15:0]),
.s_cyc (wb_cyc[4]),
.s_ack (wb_ack[4]),
.s_we (wb_we),
.s_clk (clk_sys),
.m_addr (ub_addr),
.m_wdata (ub_wdata),
.m_rdata (ub_rdata),
.m_cyc (ub_cyc),
.m_ack (ub_ack),
.m_we (ub_we),
.m_clk (clk_usb),
.rst (rst_usb)
);
assign wb_rdata[4][31:16] = 0;
// Core
usb #(
.EPDW(32)
) usb_I (
.pad_dp (usb_dp),
.pad_dn (usb_dn),
.pad_pu (usb_pu),
.ep_tx_addr_0 (ep_tx_addr_0),
.ep_tx_data_0 (ep_tx_data_0),
.ep_tx_we_0 (ep_tx_we_0),
.ep_rx_addr_0 (ep_rx_addr_0),
.ep_rx_data_1 (ep_rx_data_1),
.ep_rx_re_0 (ep_rx_re_0),
.ep_clk (clk_sys),
.wb_addr (ub_addr),
.wb_rdata (ub_rdata),
.wb_wdata (ub_wdata),
.wb_we (ub_we),
.wb_cyc (ub_cyc),
.wb_ack (ub_ack),
.clk (clk_usb),
.rst (rst_usb)
);
// LED matrix controller [5]
// ---------------------
led_ctrl led_I (
.led_a (led_a),
.led_c (led_c),
.led_clk (clk_led),
.led_rst (rst_led),
.trig_out (wakeup),
.wb_addr (wb_addr[15:0]),
.wb_rdata (wb_rdata[5]),
.wb_wdata (wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc[5]),
.wb_ack (wb_ack[5]),
.wb_clk (clk_sys),
.wb_rst (rst_sys)
);
// CRG
// ---
`ifdef SIM
sysmgr_sim crg_I (
`else
sysmgr crg_I (
`endif
.sys_start (sys_start),
.sys_stop (sys_stop),
.usb_ena (usb_ena),
.clk_led (clk_led),
.rst_led (rst_led),
.clk_sys (clk_sys),
.rst_sys (rst_sys),
.clk_usb (clk_usb),
.rst_usb (rst_usb)
);
// Unused
// ------
assign hp_p = clk_sys;
assign hp_n = 1'bz;
endmodule // top

34
gateware/rtl/xclk_cnt.v Normal file
View File

@ -0,0 +1,34 @@
/*
* xclk_cnt.v
*
* vim: ts=4 sw=4
*
* Helper to pass a counter value from one domain to another
* (assuming the counter is increment/decrement/wrap only !)
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module xclk_cnt #(
parameter integer WIDTH = 4
)(
input wire [WIDTH-1:0] i_val,
input wire i_clk,
output wire [WIDTH-1:0] o_val,
input wire o_clk,
input wire rst
);
// Encode to gray in source domain
// Capture in destination domain
// Decode from gray in destination domain
// FIXME
assign o_val = i_val;
endmodule // xclk_cnt

39
gateware/rtl/xclk_pulse.v Normal file
View File

@ -0,0 +1,39 @@
/*
* xclk_pulse.v
*
* vim: ts=4 sw=4
*
* Helper to cross a pulse from one clock domain to another
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module xclk_pulse (
input wire i_stb,
input wire i_clk,
output wire o_stb,
input wire o_clk,
input wire rst
);
reg i_toggle;
reg [2:0] o_sync;
always @(posedge i_clk or posedge rst)
if (rst)
i_toggle <= 1'b0;
else
i_toggle <= i_toggle ^ i_stb;
always @(posedge o_clk or posedge rst)
if (rst)
o_sync <= 3'b000;
else
o_sync <= { o_sync[1] ^ o_sync[0], o_sync[0], i_toggle };
assign o_stb = o_sync[2];
endmodule // xclk_pulse

154
gateware/sim/led_ctrl_tb.v Normal file
View File

@ -0,0 +1,154 @@
/*
* led_ctrl_tb.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2022-2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
`timescale 1ns / 100ps
module led_ctrl_tb;
// Signals
// -------
// DUT
wire [13:0] led_a;
wire [2:0] led_c;
wire trig_out;
// Wishbone interface
reg [31:0] wb_wdata;
wire [31:0] wb_rdata;
reg [15:0] wb_addr;
reg wb_we;
reg wb_cyc;
wire wb_ack;
// Clocks / Sync
reg pll_lock = 1'b0;
reg led_clk = 1'b0;
wire led_rst;
reg [3:0] led_rst_cnt = 4'h8;
reg wb_clk = 1'b0;
wire wb_rst;
reg [3:0] wb_rst_cnt = 4'h8;
// Recording setup
// ---------------
initial begin
$dumpfile("led_ctrl_tb.vcd");
$dumpvars(0,led_ctrl_tb);
end
// DUT
// ---
led_ctrl dut_I (
.led_a (led_a),
.led_c (led_c),
.led_clk (led_clk),
.led_rst (led_rst),
.trig_out (trig_out),
.wb_addr (wb_addr),
.wb_rdata (wb_rdata),
.wb_wdata (wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc),
.wb_ack (wb_ack),
.wb_clk (wb_clk),
.wb_rst (wb_rst)
);
// Stimulus
// --------
task wb_write;
input [15:0] addr;
input [31:0] data;
begin
wb_addr <= addr;
wb_wdata <= data;
wb_we <= 1'b1;
wb_cyc <= 1'b1;
while (~wb_ack)
@(posedge wb_clk);
wb_addr <= 4'hx;
wb_wdata <= 32'hxxxxxxxx;
wb_we <= 1'bx;
wb_cyc <= 1'b0;
@(posedge wb_clk);
end
endtask
initial begin
// Defaults
wb_addr <= 4'hx;
wb_wdata <= 32'hxxxxxxxx;
wb_we <= 1'bx;
wb_cyc <= 1'b0;
@(negedge wb_rst);
@(negedge led_rst);
@(posedge wb_clk);
// Write to frame memory
// Anode 4
// C0 [00-7f] (all ON)
// C1 (all OFF)
// C2 [40-4f] (16 cycles)
wb_write(16'h0200, 32'h7f_00_00_04);
wb_write(16'h0201, 32'h4f_40_00_7f);
wb_write(16'h0202, 32'h01_01_00_0a);
wb_write(16'h0203, 32'h01_00_00_01);
// Enable
wb_write(16'h0000, 32'hc0000007);
end
// Clock / Reset
// -------------
// Clocks
initial begin
# 200 pll_lock = 1'b1;
# 1000000 $finish;
//# 35000000 $finish;
end
always #20 wb_clk = ~wb_clk; // 25 MHz
always #83 led_clk = ~led_clk; // 6 MHz
// Reset
always @(posedge wb_clk or negedge pll_lock)
if (~pll_lock)
wb_rst_cnt <= 4'h8;
else if (wb_rst_cnt[3])
wb_rst_cnt <= wb_rst_cnt + 1;
assign wb_rst = wb_rst_cnt[3];
always @(posedge led_clk or negedge pll_lock)
if (~pll_lock)
led_rst_cnt <= 4'h8;
else if (led_rst_cnt[3])
led_rst_cnt <= led_rst_cnt + 1;
assign led_rst = led_rst_cnt[3];
endmodule

425
gateware/sim/spiflash.v Normal file
View File

@ -0,0 +1,425 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
`timescale 1 ns / 1 ps
//
// Simple SPI flash simulation model
//
// This model samples io input signals 1ns before the SPI clock edge and
// updates output signals 1ns after the SPI clock edge.
//
// Supported commands:
// AB, B9, FF, 03, BB, EB, ED
//
// Well written SPI flash data sheets:
// Cypress S25FL064L http://www.cypress.com/file/316661/download
// Cypress S25FL128L http://www.cypress.com/file/316171/download
//
// SPI flash used on iCEBreaker board:
// https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
//
module spiflash (
input csb,
input clk,
inout io0, // MOSI
inout io1, // MISO
inout io2,
inout io3
);
localparam verbose = 1;
localparam integer latency = 8;
reg [7:0] buffer;
integer bitcount = 0;
integer bytecount = 0;
integer dummycount = 0;
reg [7:0] spi_cmd;
reg [7:0] xip_cmd = 0;
reg [23:0] spi_addr;
reg [7:0] spi_in;
reg [7:0] spi_out;
reg spi_io_vld;
reg powered_up = 1;
localparam [3:0] mode_spi = 1;
localparam [3:0] mode_dspi_rd = 2;
localparam [3:0] mode_dspi_wr = 3;
localparam [3:0] mode_qspi_rd = 4;
localparam [3:0] mode_qspi_wr = 5;
localparam [3:0] mode_qspi_ddr_rd = 6;
localparam [3:0] mode_qspi_ddr_wr = 7;
reg [3:0] mode = 0;
reg [3:0] next_mode = 0;
reg io0_oe = 0;
reg io1_oe = 0;
reg io2_oe = 0;
reg io3_oe = 0;
reg io0_dout = 0;
reg io1_dout = 0;
reg io2_dout = 0;
reg io3_dout = 0;
assign #1 io0 = io0_oe ? io0_dout : 1'bz;
assign #1 io1 = io1_oe ? io1_dout : 1'bz;
assign #1 io2 = io2_oe ? io2_dout : 1'bz;
assign #1 io3 = io3_oe ? io3_dout : 1'bz;
wire io0_delayed;
wire io1_delayed;
wire io2_delayed;
wire io3_delayed;
assign #1 io0_delayed = io0;
assign #1 io1_delayed = io1;
assign #1 io2_delayed = io2;
assign #1 io3_delayed = io3;
// 16 MB (128Mb) Flash
reg [7:0] memory [0:16*1024*1024-1];
reg [1023:0] firmware_file;
initial begin
if (!$value$plusargs("firmware=%s", firmware_file))
firmware_file = "firmware.hex";
$readmemh(firmware_file, memory);
end
task spi_action;
begin
spi_in = buffer;
if (bytecount == 1) begin
spi_cmd = buffer;
if (spi_cmd == 8'h ab)
powered_up = 1;
if (spi_cmd == 8'h b9)
powered_up = 0;
if (spi_cmd == 8'h ff)
xip_cmd = 0;
end
if (powered_up && spi_cmd == 'h 03) begin
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount >= 4) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
if (powered_up && spi_cmd == 'h 0b) begin
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount >= 5) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
if (powered_up && spi_cmd == 'h bb) begin
if (bytecount == 1)
mode = mode_dspi_rd;
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount == 5) begin
xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00;
mode = mode_dspi_wr;
dummycount = latency;
end
if (bytecount >= 5) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
if (powered_up && spi_cmd == 'h eb) begin
if (bytecount == 1)
mode = mode_qspi_rd;
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount == 5) begin
xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00;
mode = mode_qspi_wr;
dummycount = latency;
end
if (bytecount >= 5) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
if (powered_up && spi_cmd == 'h ed) begin
if (bytecount == 1)
next_mode = mode_qspi_ddr_rd;
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount == 5) begin
xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00;
mode = mode_qspi_ddr_wr;
dummycount = latency;
end
if (bytecount >= 5) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
spi_out = buffer;
spi_io_vld = 1;
if (verbose) begin
if (bytecount == 1)
$write("<SPI-START>");
$write("<SPI:%02x:%02x>", spi_in, spi_out);
end
end
endtask
task ddr_rd_edge;
begin
buffer = {buffer, io3_delayed, io2_delayed, io1_delayed, io0_delayed};
bitcount = bitcount + 4;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
endtask
task ddr_wr_edge;
begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 1;
io3_oe = 1;
io0_dout = buffer[4];
io1_dout = buffer[5];
io2_dout = buffer[6];
io3_dout = buffer[7];
buffer = {buffer, 4'h 0};
bitcount = bitcount + 4;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
endtask
always @(csb) begin
if (csb) begin
if (verbose) begin
$display("");
$fflush;
end
buffer = 0;
bitcount = 0;
bytecount = 0;
mode = mode_spi;
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end else
if (xip_cmd) begin
buffer = xip_cmd;
bitcount = 0;
bytecount = 1;
spi_action;
end
end
always @(csb, clk) begin
spi_io_vld = 0;
if (!csb && !clk) begin
if (dummycount > 0) begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end else
case (mode)
mode_spi: begin
io0_oe = 0;
io1_oe = 1;
io2_oe = 0;
io3_oe = 0;
io1_dout = buffer[7];
end
mode_dspi_rd: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_dspi_wr: begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 0;
io3_oe = 0;
io0_dout = buffer[6];
io1_dout = buffer[7];
end
mode_qspi_rd: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_qspi_wr: begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 1;
io3_oe = 1;
io0_dout = buffer[4];
io1_dout = buffer[5];
io2_dout = buffer[6];
io3_dout = buffer[7];
end
mode_qspi_ddr_rd: begin
ddr_rd_edge;
end
mode_qspi_ddr_wr: begin
ddr_wr_edge;
end
endcase
if (next_mode) begin
case (next_mode)
mode_qspi_ddr_rd: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_qspi_ddr_wr: begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 1;
io3_oe = 1;
io0_dout = buffer[4];
io1_dout = buffer[5];
io2_dout = buffer[6];
io3_dout = buffer[7];
end
endcase
mode = next_mode;
next_mode = 0;
end
end
end
always @(posedge clk) begin
if (!csb) begin
if (dummycount > 0) begin
dummycount = dummycount - 1;
end else
case (mode)
mode_spi: begin
buffer = {buffer, io0};
bitcount = bitcount + 1;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
mode_dspi_rd, mode_dspi_wr: begin
buffer = {buffer, io1, io0};
bitcount = bitcount + 2;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
mode_qspi_rd, mode_qspi_wr: begin
buffer = {buffer, io3, io2, io1, io0};
bitcount = bitcount + 4;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
mode_qspi_ddr_rd: begin
ddr_rd_edge;
end
mode_qspi_ddr_wr: begin
ddr_wr_edge;
end
endcase
end
end
endmodule

167
gateware/sim/sysmgr_sim.v Normal file
View File

@ -0,0 +1,167 @@
/*
* sysmgr_sim.v
*
* vim: ts=4 sw=4
*
* System Clock / Reset generation (simulated)
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module sysmgr_sim (
// Control
input wire sys_start,
input wire sys_stop,
input wire usb_ena,
// LED clock ( 6 MHz )
output wire clk_led,
output wire rst_led,
// System clock ( 24 MHz )
output wire clk_sys,
output wire rst_sys,
// USB ( 48 MHz )
output wire clk_usb,
output wire rst_usb
);
// Signals
// -------
reg clk_base = 1'b0;
wire pll_lock;
reg [2:0] clk_div = 3'b000;
reg clk_sys_i;
wire clk_led_i;
reg [7:0] rst_cnt = 8'h00;
reg rst_i = 1;
wire rst_gbuf;
reg [2:0] sys_start_s;
reg [2:0] sys_stop_s;
wire usb_rst_trig;
reg [3:0] usb_rst_cnt;
wire usb_rst_i;
reg [3:0] usb_ena_dly;
wire usb_ena_i;
reg sys_off;
// SB_HFOSC
// -------
// Generates 48 MHz
always #10.42 clk_base = ~clk_base;
// PLL
// ---
assign clk_usb = usb_ena_i & clk_base;
assign pll_lock = usb_ena_i;
// Dividers
// --------
// Counter
always @(posedge clk_base)
clk_div <= clk_div + 1;
// SYS is div-by-2 + gated
always @(posedge clk_base)
clk_sys_i <= clk_div[0] & ~sys_off;
// LED is div-by-8
assign clk_led_i = clk_div[2];
// Global buffers
SB_GB clk_sys_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_sys_i),
.GLOBAL_BUFFER_OUTPUT(clk_sys)
);
SB_GB clk_led_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_led_i),
.GLOBAL_BUFFER_OUTPUT(clk_led)
);
// USB Enable
// ----------
// Delay falling latch signal
always @(posedge clk_base)
usb_ena_dly <= usb_ena ? 4'b1111 : { usb_ena_dly[2:0], 1'b0 };
assign usb_ena_i = usb_ena_dly[3];
// USB Reset
// ---------
// Reset trigger
assign usb_rst_trig = ~usb_ena | ~pll_lock;
// Reset counter
always @(posedge clk_usb or posedge usb_rst_trig)
if (usb_rst_trig)
usb_rst_cnt <= 4'h8;
else
usb_rst_cnt <= usb_rst_cnt + usb_rst_i;
assign usb_rst_i = usb_rst_cnt[3];
// Global buffer
SB_GB rst_usb_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(usb_rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_usb)
);
// SYS Enable
// ----------
// Synch triggers
always @(posedge clk_base)
begin
sys_start_s <= { ~sys_start_s[1] & sys_start_s[0], sys_start_s[0], sys_start };
sys_stop_s <= { ~sys_stop_s[1] & sys_stop_s[0], sys_stop_s[0], sys_stop };
end
always @(posedge clk_base)
sys_off <= (sys_off & ~rst_i & ~sys_start_s[2]) | sys_stop_s[2];
// Reset LED / SYS
// ---------------
// Counter
always @(posedge clk_led)
rst_cnt <= rst_cnt + rst_i;
always @(posedge clk_led)
rst_i <= (rst_cnt[7:4] != 4'hf);
// Global buffer
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_gbuf)
);
// Distribution
assign rst_led = rst_gbuf;
assign rst_sys = rst_gbuf;
endmodule // sysmgr

64
gateware/sim/top_tb.v Normal file
View File

@ -0,0 +1,64 @@
/*
* top_tb.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module top_tb;
// Signals
// -------
wire [3:0] spi_io;
wire spi_cs_n;
wire spi_clk;
wire usb_dp;
wire usb_dn;
wire usb_pu;
// Setup recording
// ---------------
initial begin
$dumpfile("top_tb.vcd");
$dumpvars(0,top_tb);
# 2000000 $finish;
end
// DUT
// ---
top dut_I (
.spi_io (spi_io),
.spi_clk (spi_clk),
.spi_cs_n (spi_cs_n),
.usb_dp (usb_dp),
.usb_dn (usb_dn),
.usb_pu (usb_pu)
);
// Support
// -------
pullup(usb_dp);
pullup(usb_dn);
spiflash flash_I (
.csb (spi_cs_n),
.clk (spi_clk),
.io0 (spi_io[0]),
.io1 (spi_io[1]),
.io2 (spi_io[2]),
.io3 (spi_io[3])
);
endmodule // top_tb