gateware: Initial import of the FPGA gateware

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
master
Sylvain Munaut 1 week ago
parent bf1310fd5d
commit c85dc29b06

12
.gitmodules vendored

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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