gateware/e1-tracer: Initial import

This is the project specific to the e1-tracer board that was
initially based on the iCEpick with a couple dev boards attached
but eventually consolidated to a proper board, which still retaining
100% electrical compatibility (and thus same gateware and firmware)

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2020-09-14 10:12:56 +02:00
parent da65157363
commit 546493e606
8 changed files with 737 additions and 0 deletions

4
gateware/e1-tracer/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build-tmp
__pycache__
*.vcd
.*.swp

View File

@ -0,0 +1,43 @@
# Project config
PROJ=e1-tracer
PROJ_DEPS := no2e1 no2ice40 no2misc no2usb
PROJ_RTL_SRCS := $(addprefix rtl/, \
misc.v \
sysmgr.v \
)
PROJ_RTL_SRCS += $(addprefix ../common/rtl/, \
dfu_helper.v \
picorv32.v \
soc_base.v \
soc_bram.v \
soc_iobuf.v \
soc_picorv32_bridge.v \
soc_spram.v \
wb_arbiter.v \
wb_dma.v \
wb_epbuf.v \
)
PROJ_PREREQ = \
$(BUILD_TMP)/boot.hex \
$(NULL)
PROJ_TOP_SRC := rtl/top.v
PROJ_TOP_MOD := top
# Target config
BOARD ?= e1-tracer
DEVICE = up5k
PACKAGE = sg48
NEXTPNR_ARGS = --pre-pack data/clocks.py --seed 19
# Include default rules
NO2BUILD_DIR := ../build
include $(NO2BUILD_DIR)/project-rules.mk
# Custom rules
../common/fw/boot.hex:
make -C ../common/fw boot.hex
$(BUILD_TMP)/boot.hex: ../common/fw/boot.hex
cp $< $@

View File

@ -0,0 +1,2 @@
ctx.addClock("clk_sys", 24)
ctx.addClock("clk_48m", 48)

View File

@ -0,0 +1,40 @@
# LIU data
set_io -pullup yes e1A_rx_data 45 # B1
set_io -pullup yes e1A_rx_clk 44 # B0
set_io -pullup yes e1B_rx_data 47 # B3
set_io -pullup yes e1B_rx_clk 46 # B2
# LIU control
set_io -pullup yes liu_mosi 31 # A0
set_io -pullup yes liu_miso 32 # A1
set_io -pullup yes liu_clk 34 # A2
set_io -pullup yes liu_cs_n[0] 42 # A4
set_io -pullup yes liu_cs_n[1] 43 # A5
# USB
set_io usb_dp 10
set_io usb_dn 9
set_io usb_pu 6
# Flash
set_io -pullup yes flash_mosi 14
set_io -pullup yes flash_miso 17
set_io -pullup yes flash_clk 15
set_io -pullup yes flash_cs_n 16
# Vio
set_io -pullup yes vio_pdm 19
# Button
set_io -pullup yes btn 25
# Clock
set_io clk_in 20
# Debug UART
set_io -pullup yes dbg_tx 38
# Leds
set_io rgb[0] 39
set_io rgb[1] 40
set_io rgb[2] 41

View File

@ -0,0 +1,187 @@
e1-tracer SoC Memory Map
========================
Overview
--------
| Base | Size | Description | IP Core doc
|-------------|------------------|-----------------|-------------
|`0x00000000` | `0x00400` (1k) | Boot ROM |
|`0x00020000` | `0x10000` (64k) | Main SRAM |
|`0x80000000` | | Flash SPI | [`no2ice40/ice40_spi_wb`](../../cores/no2ice40/doc/ice40_spi_wb.md)
|`0x81000000` | | Debug UART | [`no2misc/uart_wb`](../../cores/no2misc/doc/uart_wb.md)
|`0x82000000` | | RGB LED | [`no2ice40/ice40_rgb_wb`](../../cores/no2ice40/doc/ice40_rgb_wb.md)
|`0x83000000` | | USB core | [`no2usb`](../../cores/no2usb/doc/mem-map.md)
|`0x84000000` | `0x01000` (4k) | USB data buffer |
|`0x85000000` | `0x10000` (64k) | E1 data buffer |
|`0x86000000` | | DMA | See below
|`0x87000000` | | E1 core | [`no2e1`](../../cores/no2e1/doc/mem-map.md). See notes below.
|`0x88000000` | | Misc | See below
|`0x89000000` | | LIU SPI | [`no2ice40/ice40_spi_wb`](../../cores/no2ice40/doc/ice40_spi_wb.md)
Memory
------
### `0x00000000`: Boot ROM
This memory zone is initialized directly in the FPGA bitstream itself.
It contains the bootstrap program that will load the main firmware from
flash into the main SRAM and jump to it.
### `0x00020000`: Main SRAM
The main SoC SRAM, implemented using the UP5k SPRAMs. It can't be initialized
in the FPGA bitstream directly, hence the need for the Boot ROM zone.
### `0x84000000`: USB data buffer
This 4k zone actually correspond to 8k of SRAM inside the USB core.
The RX and TX buffer for the USB are distinct and are accessed independently
depending if read or write access are made to this zone. (RX buffer can only
be read, TX buffer can only be written). All accesses must also be 32 bit
wide !
### `0x85000000`: E1 data buffer
This 64k buffer is reserved for E1 data and is what is used by the E1 core.
Address mapping is :
```text
,---------------------------------------------------------------,
| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------------------------------------------------------|
| multi-frame | frame | timeslot |
'---------------------------------------------------------------'
```
and the `multi-frame` number is what is exchanged in the E1 core buffer
descriptors.
Custom Peripherals
------------------
### `0x86000000`: DMA
Very simple DMA core allowing direct copy of data between the USB and E1 data
buffers.
#### Status (Read Only, addr `0x00`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | b| d| /| len |
'-----------------------------------------------------------------------------------------------'
* [ 15] - b : Busy flag
* [ 14] - d : Direction ( 0=E1 to USB, 1=USB to E1 )
* [12: 0] - len : Remaining length of the in-progress transfer ( -1 = done )
```
#### Transfer start/direction/length (Write Only, addr `0x00`)
Write to this register initiate the transfer.
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | d| / | len |
'-----------------------------------------------------------------------------------------------'
* [ 14] - d : Direction ( 0=E1 to USB, 1=USB to E1 )
* [11: 0] - len : Transfer length ( Number of Words - 2 )
```
#### E1 buffer offset (Write Only, addr `0x08`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | e1_ofs |
'-----------------------------------------------------------------------------------------------'
* [13:0] - e1_ofs
```
Word offset inside the E1 data buffer where the next transfer will start
#### USB buffer offset (Write Only, addr `0x0C`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | usb_ofs |
'-----------------------------------------------------------------------------------------------'
* [9:0] - usb_ofs
```
Word offset inside the USB End Point buffer where the next transfer will start
### `0x87000000`: E1 core
Refer to the [`no2e1` core documentation](../../cores/no2e1/doc/mem-map.md)
for register description.
The core instanciated here has a 2 channels with only RX units present.
### `0x88000000`: Misc
Collection of small auxiliary peripherals.
#### Boot (Write Only, addr `0x00`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | x| sel |
'-----------------------------------------------------------------------------------------------'
* [ 2] - d : boot eXecute
* [11:0] - sel : boot select
```
Write to this register with bit 2 set will trigger a FPGA reload of the selected image.
#### E1 tick channel 0/1 (Read Only, addr `0x04` / `0x05`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| / | rx_tick |
'-----------------------------------------------------------------------------------------------'
* [15:0] - rx_tick
```
An internal counter is incremented at every bit received by the corresponding E1 channel. That
counter value is then captured at every USB Start-of-Frame packet and the last captured value
made available here.
#### Time (Read Only, addr `0x07`)
```text
,-----------------------------------------------------------------------------------------------,
|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|-----------------------------------------------------------------------------------------------|
| time |
'-----------------------------------------------------------------------------------------------'
* [31:0] - time
```
32 bit counter incremented at the system clock rate ( 24 MHz )

View File

@ -0,0 +1,138 @@
/*
* misc.v
*
* vim: ts=4 sw=4
*
* Misc peripheral functions
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-S-2.0
*/
`default_nettype none
module misc (
// Button
input wire btn,
// Ticks
input wire [1:0] tick_e1_rx,
input wire tick_usb_sof,
// Reset request
output wire rst_req,
// Wishbone
input wire [ 7: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,
// Clock / Reset
input wire clk,
input wire rst
);
// Signals
// -------
genvar i;
// Bus
wire bus_clr;
reg bus_we_boot;
// Counters
reg [15:0] cnt_e1_rx[0:1];
reg [15:0] cap_e1_rx[0:1];
reg [31:0] cnt_time;
// Boot
reg [1:0] boot_sel;
reg boot_now;
// Bus interface
// -------------
// Ack
always @(posedge clk)
wb_ack <= wb_cyc & ~wb_ack;
assign bus_clr = ~wb_cyc | wb_ack;
// Write enables
always @(posedge clk)
if (bus_clr | ~wb_we)
bus_we_boot <= 1'b0;
else
bus_we_boot <= wb_addr == 4'h0;
// Read mux
always @(posedge clk)
if (bus_clr)
wb_rdata <= 32'h00000000;
else
case (wb_addr[3:0])
4'h4: wb_rdata <= { cap_e1_rx[0], 16'h0000 };
4'h5: wb_rdata <= { cap_e1_rx[1], 16'h0000 };
4'h7: wb_rdata <= cnt_time;
default: wb_rdata <= 32'hxxxxxxxx;
endcase
// Counters
// --------
// E1 ticks
for (i=0; i<2; i=i+1) begin
always @(posedge clk or posedge rst)
if (rst)
cnt_e1_rx[i] <= 16'h0000;
else if (tick_e1_rx[i])
cnt_e1_rx[i] <= cnt_e1_rx[i] + 1;
always @(posedge clk)
if (tick_usb_sof)
cap_e1_rx[i] <= cnt_e1_rx[i];
end
// Time counter
always @(posedge clk)
if (rst)
cnt_time <= 32'h00000000;
else
cnt_time <= cnt_time + 1;
// DFU / Reboot
// ------------
always @(posedge clk or posedge rst)
if (rst) begin
boot_now <= 1'b0;
boot_sel <= 2'b00;
end else if (bus_we_boot) begin
boot_now <= wb_wdata[2];
boot_sel <= wb_wdata[1:0];
end
dfu_helper #(
.TIMER_WIDTH(26),
.BTN_MODE(3),
.DFU_MODE(0)
) dfu_I (
.boot_sel(boot_sel),
.boot_now(boot_now),
.btn_pad (btn),
.btn_val (),
.rst_req (rst_req),
.clk (clk),
.rst (rst)
);
endmodule // misc

View File

@ -0,0 +1,97 @@
/*
* sysmgr.v
*
* vim: ts=4 sw=4
*
* System Clock / Reset generation
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-S-2.0
*/
`default_nettype none
module sysmgr (
input wire clk_in,
input wire rst_in,
output wire clk_sys,
output wire rst_sys,
output wire clk_48m,
output wire rst_48m
);
// Signals
wire pll_lock;
wire pll_reset_n;
wire clk_12m_i;
wire clk_24m_i;
wire clk_48m_i;
wire rst_i;
wire rst_out;
reg [3:0] rst_cnt;
// Global input buffer for 12 MHz clock
SB_GB_IO #(
.PIN_TYPE(6'b000001)
) gb_in (
.PACKAGE_PIN(clk_in),
.GLOBAL_BUFFER_OUTPUT(clk_12m_i),
);
// PLL instance
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_HALF"),
.ENABLE_ICEGATE_PORTA(1'b0),
.ENABLE_ICEGATE_PORTB(1'b0)
) pll_I (
.REFERENCECLK(clk_12m_i),
.PLLOUTCOREA(),
.PLLOUTGLOBALA(clk_48m_i),
.PLLOUTCOREB(),
.PLLOUTGLOBALB(clk_24m_i),
.EXTFEEDBACK(1'b0),
.DYNAMICDELAY(8'h00),
.RESETB(pll_reset_n),
.BYPASS(1'b0),
.LATCHINPUTVALUE(1'b0),
.LOCK(pll_lock),
.SDI(1'b0),
.SDO(),
.SCLK(1'b0)
);
assign clk_sys = clk_24m_i;
assign clk_48m = clk_48m_i;
// PLL reset generation
assign pll_reset_n = ~rst_in;
// Logic reset generation
always @(posedge clk_24m_i or negedge pll_lock)
if (!pll_lock)
rst_cnt <= 4'h0;
else if (~rst_cnt[3])
rst_cnt <= rst_cnt + 1;
assign rst_i = ~rst_cnt[3];
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_out)
);
assign rst_sys = rst_out;
assign rst_48m = rst_out;
endmodule // sysmgr

View File

@ -0,0 +1,226 @@
/*
* top.v
*
* vim: ts=4 sw=4
*
* Top-level for the e1-tracer boards.
*
* Note that some things here are only to maintain bitstream compatibility
* with the icepick based proto setup.
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-S-2.0
*/
`default_nettype none
module top (
// LIU data
input wire e1A_rx_data,
input wire e1A_rx_clk,
input wire e1B_rx_data,
input wire e1B_rx_clk,
// LIU control
inout wire liu_mosi,
inout wire liu_miso,
inout wire liu_clk,
inout wire [1:0] liu_cs_n,
// USB
inout wire usb_dp,
inout wire usb_dn,
output wire usb_pu,
// Flash
inout wire flash_mosi,
inout wire flash_miso,
inout wire flash_clk,
inout wire flash_cs_n,
// VIO PDM
output wire vio_pdm,
// Button
input wire btn,
// Clock (12 MHz)
input wire clk_in,
// Debug UART
output wire dbg_tx,
// RGB LEDs
output wire [2:0] rgb
);
localparam integer WB_N = 2;
genvar i;
// Signals
// -------
// Flash SPI internal signals
wire flash_mosi_i, flash_miso_i, flash_clk_i;
wire flash_mosi_o, flash_miso_o, flash_clk_o;
wire flash_mosi_oe, flash_miso_oe, flash_clk_oe;
wire flash_csn_o;
// Peripheral wishbone
wire [15:0] wb_addr;
wire [31:0] wb_rdata [0:WB_N-1];
wire [31:0] wb_wdata;
wire [ 3:0] wb_wmsk;
wire wb_we;
wire [WB_N-1:0] wb_cyc;
wire [WB_N-1:0] wb_ack;
wire [(WB_N*32)-1:0] wb_rdata_flat;
// Ticks
wire [1:0] tick_e1_rx;
wire tick_usb_sof;
// Clocks / Reset
wire rst_req;
wire clk_sys;
wire rst_sys;
wire clk_48m;
wire rst_48m;
// SoC base
// --------
// Instance
soc_base #(
.WB_N(WB_N),
.E1_N(2),
.E1_UNIT_HAS_RX(2'b11),
.E1_UNIT_HAS_TX(2'b00),
.E1_LIU(1)
) soc_I (
.e1_rx_hi_p (),
.e1_rx_hi_n (),
.e1_rx_lo_p (),
.e1_rx_lo_n (),
.e1_tx_hi (),
.e1_tx_lo (),
.e1_rx_data ({e1B_rx_data, e1A_rx_data}),
.e1_rx_clk ({e1B_rx_clk, e1A_rx_clk }),
.e1_tx_data (),
.e1_tx_clk (),
.usb_dp (usb_dp),
.usb_dn (usb_dn),
.usb_pu (usb_pu),
.flash_mosi_i (flash_mosi_i),
.flash_mosi_o (flash_mosi_o),
.flash_mosi_oe(flash_mosi_oe),
.flash_miso_i (flash_miso_i),
.flash_miso_o (flash_miso_o),
.flash_miso_oe(flash_miso_oe),
.flash_clk_i (flash_clk_i),
.flash_clk_o (flash_clk_o),
.flash_clk_oe (flash_clk_oe),
.flash_csn_o (flash_csn_o),
.dbg_rx (1'b1),
.dbg_tx (dbg_tx),
.rgb (rgb),
.wb_m_addr (wb_addr),
.wb_m_rdata (wb_rdata_flat),
.wb_m_wdata (wb_wdata),
.wb_m_wmsk (wb_wmsk),
.wb_m_we (wb_we),
.wb_m_cyc (wb_cyc),
.wb_m_ack (wb_ack),
.tick_e1_rx (tick_e1_rx),
.tick_usb_sof (tick_usb_sof),
.clk_sys (clk_sys),
.rst_sys (rst_sys),
.clk_48m (clk_48m),
.rst_48m (rst_48m)
);
// WB read data flattening
for (i=0; i<WB_N; i=i+1)
assign wb_rdata_flat[i*32+:32] = wb_rdata[i];
// SPI IO
SB_IO #(
.PIN_TYPE(6'b101001),
.PULLUP(1'b1)
) spi_io_I[2:0] (
.PACKAGE_PIN ({flash_mosi, flash_miso, flash_clk }),
.OUTPUT_ENABLE({flash_mosi_oe, flash_miso_oe, flash_clk_oe}),
.D_OUT_0 ({flash_mosi_o, flash_miso_o, flash_clk_o }),
.D_IN_0 ({flash_mosi_i, flash_miso_i, flash_clk_i })
);
assign flash_cs_n = flash_csn_o;
// Misc [0]
// ----
misc misc_I (
.btn (btn),
.tick_e1_rx (tick_e1_rx),
.tick_usb_sof (tick_usb_sof),
.rst_req (rst_req),
.wb_addr (wb_addr[7:0]),
.wb_rdata (wb_rdata[0]),
.wb_wdata (wb_wdata),
.wb_we (wb_we),
.wb_cyc (wb_cyc[0]),
.wb_ack (wb_ack[0]),
.clk (clk_sys),
.rst (rst_sys)
);
// LIU SPI [1]
// -------
ice40_spi_wb #(
.N_CS(2),
.WITH_IOB(1),
.UNIT(1)
) spi_I (
.pad_mosi (liu_mosi),
.pad_miso (liu_miso),
.pad_clk (liu_clk),
.pad_csn (liu_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)
);
// Vio PDM
// -------
// Compat with iCEpick
assign vio_pdm = 1'b1;
// Clock / Reset
// -------------
sysmgr sys_mgr_I (
.clk_in (clk_in),
.rst_in (rst_req),
.clk_sys(clk_sys),
.rst_sys(rst_sys),
.clk_48m(clk_48m),
.rst_48m(rst_48m)
);
endmodule // top