Skip to content

Commit

Permalink
gateware: add support for ECP5 / Colorlight i5 (#17)
Browse files Browse the repository at this point in the history
* gateware: basic demo working for ECP5/Colorlight i5

* gateware/ecp5: add sysmgr, UART and reset button works on Colorlight i5

* gateware/make: unify build systems for ecp5/ice40

* gateware: update README with new build flow
  • Loading branch information
vk2seb authored Mar 30, 2023
1 parent 4dd77ff commit 3a9e48b
Show file tree
Hide file tree
Showing 35 changed files with 526 additions and 456 deletions.
17 changes: 15 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@ name: build & test
on: [push, pull_request]

jobs:
build-bitstream:
build-bitstream-icebreaker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: YosysHQ/setup-oss-cad-suite@v1
- run: git submodule update --init gateware/external/no2misc
- run: yosys --version
- run: make -C gateware
- run: make BOARD=icebreaker -C gateware
- uses: actions/upload-artifact@v3
with:
name: eurorack-pmod-bitstream.bin
path: gateware/top.bin

build-bitstream-colorlight-i5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: YosysHQ/setup-oss-cad-suite@v1
- run: git submodule update --init gateware/external/no2misc
- run: yosys --version
- run: make BOARD=colorlight_i5 -C gateware
- uses: actions/upload-artifact@v3
with:
name: eurorack-pmod-bitstream.bin
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ For a high-level overview on R2.2 hardware, **see [my FOSDEM '23 talk](https://y

### This project is:
- The design for a Eurorack-compatible PCB and front-panel, including a [PMOD](https://en.wikipedia.org/wiki/Pmod_Interface) connector (compatible with most FPGA dev boards). PCB designed in [KiCAD](https://www.kicad.org/). Design is [certified open hardware](https://certification.oshwa.org/de000135.html).
- Various [example cores](gateware/cores) (and calibration / driver cores for the audio CODEC) initially targeting an [iCEBreaker FPGA](https://1bitsquared.com/products/icebreaker). Examples include calibration, sampling, effects, synthesis sources and so on. The design files can be synthesized to a bitstream using Yosys' [oss-cad-suite](https://github.com/YosysHQ/oss-cad-suite-build).
- Various [example cores](gateware/cores) (and calibration / driver cores for the audio CODEC) initially targeting an [iCEBreaker FPGA](https://1bitsquared.com/products/icebreaker) (iCE40 part) and Colorlight i5 (ECP5 part). Examples include calibration, sampling, effects, synthesis sources and so on. The design files can be synthesized to a bitstream using Yosys' [oss-cad-suite](https://github.com/YosysHQ/oss-cad-suite-build).
- A [VCV Rack plugin](https://github.com/schnommus/verilog-vcvrack) so you can simulate your Verilog designs in a completely virtual modular system, no hardware required.

## Hardware details
Expand All @@ -28,16 +28,15 @@ For a high-level overview on R2.2 hardware, **see [my FOSDEM '23 talk](https://y
- I/O is about +/- 8V capable, wider is possible with a resistor change.

## Gateware details
- Examples based on Icebreaker FPGA + open-source toolchain.
- Examples based on Icebreaker FPGA (iCE40 part) or Colorlight i5 (ECP5 part).
- User-defined DSP logic is decoupled from rest of system (see [`gateware/cores`](gateware/cores) directory)

## Getting Started

The gateware is automatically built and tested in CI, so it may be helpful to look at [`.github/workflows/main.yml`](.github/workflows/main.yml). Basically the reality of working with this device is as follows:

1. Build or obtain `eurorack-pmod` hardware and connect it to your FPGA development board using a ribbon cable or similar. (Make sure to check the `RIBBON` define in `top.sv` is set correctly!)
0. Install the OSS FPGA CAD flow. The gateware is automatically built and tested in CI, so it may be helpful to look at [`.github/workflows/main.yml`](.github/workflows/main.yml).
1. Build or obtain `eurorack-pmod` hardware and connect it to your FPGA development board using a ribbon cable or similar. (Double check that the pin mappings are correct, some ribbon cables will swap them on you)
2. Try some of the examples. From the `gateware` directory, type `make` to see valid commands. By default it will compile a bitstream with the 'mirror' core, which just sends inputs to outputs.
2. Calibrate your hardware using the process described in [`gateware/cal/cal.py`](gateware/cal/cal.py). Use this to create your own `gateware/cal/cal_mem.hex` to compensate for any DC biases in the ADCs/DACs. (this step is only necessary if you need sub-50mV accuracy on your inputs/outputs, which is the case if you are tuning oscillators, not so much if you are creating rhythm pulses.
3. Pick the core you want to use from `gateware/cores` and use the appropriate one in `gateware/top.sv`. Note, you can only have one enabled at a time unless you add extra connections between them!

# Project structure
The project is split into 2 directories, [`hardware`](hardware) for the PCB/panel and [`gateware`](gateware) for the FPGA source. Some interesting directories:
Expand Down
2 changes: 2 additions & 0 deletions gateware/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ sim/*/sim_build
*.bin
*.json
*.log
*.config
*.svf
55 changes: 26 additions & 29 deletions gateware/Makefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
PROJ = top
ADD_SRC = eurorack_pmod.sv \
drivers/pmod_i2c_master.sv \
drivers/ak4619.sv \
external/no2misc/rtl/uart_tx.v \
external/no2misc/rtl/i2c_master.v \
external/ice40_sysmgr.v \
cal/cal.sv \
cal/debug_uart.sv \
cores/mirror.sv \
cores/clkdiv.sv \
cores/seqswitch.sv \
cores/sampler.sv \
cores/bitcrush.sv \
cores/vca.sv \
cores/vco.sv \
cores/delay_raw.sv \
cores/delayline.sv \
cores/transpose.sv \
cores/pitch_shift.sv \
cores/echo.sv \
cores/stereo_echo.sv \
cores/filter.sv \
cores/filter/filter_svf_pipelined.sv
ALL_BOARDS = $(shell ls boards)
ALL_CORES = $(shell basename --suffix=.sv -- cores/*.sv)

PIN_DEF = mk/icebreaker.pcf
DEVICE = up5k
PACKAGE = sg48
all prog:
ifeq ($(BOARD),)
@echo "Valid BOARD values are: $(ALL_BOARDS)".
@echo "Valid CORE values are: $(ALL_CORES)".
@echo "For example:"
@echo " $$ make clean"
@echo " $$ # Build bitstream with specific core and program it"
@echo " $$ make BOARD=icebreaker CORE=stereo_echo prog"
@exit 1
endif
ifeq ($(wildcard ./boards/$(BOARD)/Makefile),)
@echo "Target '$(BOARD)' does not exist in 'boards/'"
@echo "Valid targets are: $(ALL_BOARDS)".
@exit 2
endif
mkdir -p build/$(BOARD)
# For now we always force a re-build since we can pass different DSP cores
# through environment vars and we need a re-build to happen in this case.
$(MAKE) -B -f boards/$(BOARD)/Makefile BUILD=build/$(BOARD) $(MAKECMDGOALS)

prog: iceprog
clean:
rm -rf build/

include ./mk/main.mk
.SECONDARY:
.PHONY: all prog clean
.DEFAULT_GOAL := all
17 changes: 17 additions & 0 deletions gateware/boards/colorlight_i5/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PROJ = top

CORE ?= mirror

DEVICE = 25k
PACKAGE = CABGA381
PIN_DEF = ./boards/colorlight_i5/pinmap.lpf
ADD_DEFINES = -DSELECTED_DSP_CORE=$(CORE)

include ./mk/common.mk
include ./mk/ecp5.mk

ADD_SRC = boards/colorlight_i5/sysmgr.v \
$(SRC_COMMON)

prog: $(BUILD)/$(PROJ).bin
openFPGALoader -b colorlight-i5 $(BUILD)/$(PROJ).bin
1 change: 1 addition & 0 deletions gateware/boards/colorlight_i5/defines.v
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`define ECP5
33 changes: 33 additions & 0 deletions gateware/boards/colorlight_i5/pinmap.lpf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
LOCATE COMP "CLK" SITE "P3";
IOBUF PORT "CLK" IO_TYPE=LVCMOS33;
FREQUENCY PORT "CLK" 25 MHZ;

# This pinout assumes a ribbon cable IS used and the PMOD is
# connected through an IDC ribbon to the dev board.

# These pads are PMOD_P2A
LOCATE COMP "PMOD_MCLK" SITE "K18";
LOCATE COMP "PMOD_PDN" SITE "T18";
LOCATE COMP "PMOD_I2C_SDA" SITE "R17";
LOCATE COMP "PMOD_I2C_SCL" SITE "M17";
LOCATE COMP "PMOD_SDIN1" SITE "P17";
LOCATE COMP "PMOD_SDOUT1" SITE "R18";
LOCATE COMP "PMOD_LRCK" SITE "C18";
LOCATE COMP "PMOD_BICK" SITE "U16";

IOBUF PORT "PMOD_MCLK" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_PDN" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_I2C_SDA" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_I2C_SCL" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_SDIN1" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_SDOUT1" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_LRCK" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "PMOD_BICK" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;

# This pad is PMOD_P2B_IO7
LOCATE COMP "RESET_BUTTON" SITE "M18";
IOBUF PORT "RESET_BUTTON" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;

# This is connected to the STM32 on the Colorlight development board.
LOCATE COMP "UART_TX" SITE "J17";
IOBUF PORT "UART_TX" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
76 changes: 76 additions & 0 deletions gateware/boards/colorlight_i5/sysmgr.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
`default_nettype none

module sysmgr (
// Assumed 25Mhz for Colorlight i5.
input wire clk_in,
input wire rst_in,
output wire clk_12m,
output wire rst_out
);

// Signals
wire clk_fb;

wire pll_lock;
wire pll_reset;

wire rst_i;
reg [7:0] rst_cnt;

// You can re-generate this using `ecppll` tool. Be careful, the default settings
// disable PLLRST_ENA and use a different FEEDBK_PATH, make sure they remain.

(* FREQUENCY_PIN_CLKI="25" *)
(* FREQUENCY_PIN_CLKOS="12" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
.PLLRST_ENA("ENABLED"),
.INTFB_WAKE("DISABLED"),
.STDBY_ENABLE("DISABLED"),
.DPHASE_SOURCE("DISABLED"),
.OUTDIVIDER_MUXA("DIVA"),
.OUTDIVIDER_MUXB("DIVB"),
.OUTDIVIDER_MUXC("DIVC"),
.OUTDIVIDER_MUXD("DIVD"),
.CLKI_DIV(1),
.CLKOP_ENABLE("ENABLED"),
.CLKOP_DIV(24),
.CLKOP_CPHASE(9),
.CLKOP_FPHASE(0),
.CLKOS_ENABLE("ENABLED"),
.CLKOS_DIV(50),
.CLKOS_CPHASE(0),
.CLKOS_FPHASE(0),
.FEEDBK_PATH("INT_OP"),
.CLKFB_DIV(1)
) pll_i (
.RST(pll_reset),
.STDBY(1'b0),
.CLKI(clk_in),
.CLKOS(clk_12m),
.CLKFB(clk_fb),
.CLKINTFB(clk_fb),
.PHASESEL0(1'b0),
.PHASESEL1(1'b0),
.PHASEDIR(1'b1),
.PHASESTEP(1'b1),
.PHASELOADREG(1'b1),
.PLLWAKESYNC(1'b0),
.ENCLKOP(1'b0),
.LOCK(pll_lock)
);

// PLL reset generation
assign pll_reset = rst_in;
// Logic reset generation
always @(posedge clk_in)
if (!pll_lock)
rst_cnt <= 8'h0;
else if (~rst_cnt[7])
rst_cnt <= rst_cnt + 1;

assign rst_i = ~rst_cnt[7];

assign rst_out = rst_i;

endmodule // sysmgr
17 changes: 17 additions & 0 deletions gateware/boards/icebreaker/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PROJ = top

CORE ?= mirror

DEVICE = up5k
PACKAGE = sg48
PIN_DEF = ./boards/icebreaker/pinmap.pcf
ADD_DEFINES = -DSELECTED_DSP_CORE=$(CORE) -DINVERT_BUTTON=1

include ./mk/common.mk
include ./mk/ice40.mk

ADD_SRC = boards/icebreaker/sysmgr.v \
$(SRC_COMMON)

prog: $(BUILD)/$(PROJ).bin
iceprog $(BUILD)/$(PROJ).bin
19 changes: 19 additions & 0 deletions gateware/boards/icebreaker/pinmap.pcf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 12 MHz clock
set_io -nowarn CLK 35
set_frequency CLK 12

# UART and button for debugging
set_io -nowarn UART_TX 9
# Reset button is pressed == low so we need to invert it.
set_io -nowarn RESET_BUTTON 10

# PMOD 2, assuming horizontal flip (ribbon cable
# between eurorack-pmod and PMOD connector IS in place).
set_io -nowarn PMOD_SDIN1 27
set_io -nowarn PMOD_SDOUT1 25
set_io -nowarn PMOD_LRCK 21
set_io -nowarn PMOD_BICK 19
set_io -nowarn PMOD_I2C_SCL 26
set_io -nowarn PMOD_I2C_SDA 23
set_io -nowarn PMOD_PDN 20
set_io -nowarn PMOD_MCLK 18
83 changes: 83 additions & 0 deletions gateware/boards/icebreaker/sysmgr.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
`default_nettype none

module sysmgr (
input wire clk_in,
input wire rst_in,
output wire clk_12m,
output wire rst_out
);

// Signals
wire pll_lock;
wire pll_reset_n;

wire clk_2x_i;
wire clk_1x_i;
wire rst_i;
reg [7:0] rst_cnt;

// PLL instance
`ifdef SIM
reg toggle = 1'b0;

initial
rst_cnt <= 8'h80;

always @(posedge clk_in)
toggle <= ~toggle;

assign clk_1x_i = toggle;
assign clk_2x_i = clk_in;
assign pll_lock = pll_reset_n;
`else
`ifndef VERILATOR_LINT_ONLY
SB_PLL40_2F_PAD #(
.DIVR(4'b0000),
.DIVF(7'b0111111),
.DIVQ(3'b101),
.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"),
) pll_I (
.PACKAGEPIN(clk_in),
.PLLOUTGLOBALA(clk_2x_i),
.PLLOUTGLOBALB(clk_1x_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)
);
`endif
`endif

assign clk_12m = clk_1x_i;

// PLL reset generation
assign pll_reset_n = ~rst_in;

// Logic reset generation
always @(posedge clk_1x_i or negedge pll_lock)
if (!pll_lock)
rst_cnt <= 8'h80;
else if (rst_cnt[7])
rst_cnt <= rst_cnt + 1;

assign rst_i = rst_cnt[7];

`ifndef VERILATOR_LINT_ONLY
SB_GB rst_gbuf_I (
.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
.GLOBAL_BUFFER_OUTPUT(rst_out)
);
`endif

endmodule // sysmgr
1 change: 0 additions & 1 deletion gateware/cal/cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#
# Calibration process:
# 1. Compile gateware and program FPGA with these defines in `top.sv`:
# - DEBUG_UART
# - OUTPUT_CALIBRATION
# 2. Connect +/- 5V source to all INPUTS
# 3. Run `sudo ./cal.py`
Expand Down
2 changes: 1 addition & 1 deletion gateware/cores/sampler.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module sampler #(
parameter W = 16,
parameter FP_OFFSET = 2,
parameter N_SAMPLES = 12'h690,
parameter PATH_SAMPLES = "sampler_data/clap.hex"
parameter PATH_SAMPLES = "util/sampler_data/clap.hex"
)(
input clk,
input sample_clk,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 3a9e48b

Please sign in to comment.