Open TheZoq2 opened 1 year ago
I finally got this to work by crossreferencing some stuff from https://github.com/BrunoLevy/learn-fpga
The core problem turned out to be that the dram module seems to need to run at 50MHz, and the dram clock must be pahse offset from the system clock
To make it work, for anyone reading:
Generate a dram controller
p3 litedram/gen.py --name sdram_controller examples/ulx3s.yml
Set up a custom build.sh
, lpf.lpf
and build.ys
build.sh:
set -e
yosys -l sdram_controller.rpt build.ys
nextpnr-ecp5 --json sdram_controller.json --lpf lpf.lpf --textcfg sdram_controller.config --85k --package CABGA381 --speed 6 --timing-allow-fail --seed 1
ecppack --bootaddr 0 --compress sdram_controller.config --svf build/gateware/sdram_controller.svf --bit sdram_controller.bit
build.ys
verilog_defaults -push
verilog_defaults -add -defer
read_verilog /home/frans/build/litex/pythondata-cpu-vexriscv/pythondata_cpu_vexriscv/verilog/VexRiscv.v
read_verilog /home/frans/Documents/fpga/litex/litedram/build/gateware/sdram_controller.v
read_verilog top.v
verilog_defaults -pop
attrmap -tocase keep -imap keep="true" keep=1 -imap keep="false" keep=0 -remove keep=0
synth_ecp5 -top top
write_json sdram_controller.json
lpf.lpf
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
# LOCATE COMP "ftdi_rxd" SITE "L4"; # FPGA transmits to ftdi
# LOCATE COMP "ftdi_txd" SITE "M1"; # FPGA receives from ftdi
LOCATE COMP "uart_tx" SITE "L4";
LOCATE COMP "uart_rx" SITE "M1";
# Button 1
LOCATE COMP "rst" SITE "R1"; # FIRE1
# LOCATE COMP "sdram_clk" SITE "F19";
LOCATE COMP "user_clk" SITE "F19";
IOBUF PORT "user_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
LOCATE COMP "user_rst" SITE "C10";
LOCATE COMP "clk25" SITE "G2";
IOBUF PORT "clk25" IO_TYPE=LVCMOS33;
LOCATE COMP "rst" SITE "R1";
IOBUF PORT "rst" IO_TYPE=LVCMOS33;
LOCATE COMP "sdram_clock" SITE "F19";
IOBUF PORT "sdram_clock" IO_TYPE=LVCMOS33;
LOCATE COMP "wifi_gpio0" SITE "F1";
IOBUF PORT "wifi_gpio0" IO_TYPE=LVCMOS33;
LOCATE COMP "serial_tx" SITE "L4";
IOBUF PORT "serial_tx" IO_TYPE=LVCMOS33;
LOCATE COMP "serial_rx" SITE "M1";
IOBUF PORT "serial_rx" IO_TYPE=LVCMOS33;
LOCATE COMP "sdram_a[0]" SITE "M20";
IOBUF PORT "sdram_a[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[0]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[1]" SITE "M19";
IOBUF PORT "sdram_a[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[1]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[2]" SITE "L20";
IOBUF PORT "sdram_a[2]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[2]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[3]" SITE "L19";
IOBUF PORT "sdram_a[3]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[3]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[4]" SITE "K20";
IOBUF PORT "sdram_a[4]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[4]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[5]" SITE "K19";
IOBUF PORT "sdram_a[5]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[5]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[6]" SITE "K18";
IOBUF PORT "sdram_a[6]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[6]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[7]" SITE "J20";
IOBUF PORT "sdram_a[7]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[7]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[8]" SITE "J19";
IOBUF PORT "sdram_a[8]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[8]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[9]" SITE "H20";
IOBUF PORT "sdram_a[9]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[9]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[10]" SITE "N19";
IOBUF PORT "sdram_a[10]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[10]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[11]" SITE "G20";
IOBUF PORT "sdram_a[11]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[11]" SLEWRATE=FAST;
LOCATE COMP "sdram_a[12]" SITE "G19";
IOBUF PORT "sdram_a[12]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_a[12]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[0]" SITE "J16";
IOBUF PORT "sdram_dq[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[0]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[1]" SITE "L18";
IOBUF PORT "sdram_dq[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[1]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[2]" SITE "M18";
IOBUF PORT "sdram_dq[2]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[2]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[3]" SITE "N18";
IOBUF PORT "sdram_dq[3]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[3]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[4]" SITE "P18";
IOBUF PORT "sdram_dq[4]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[4]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[5]" SITE "T18";
IOBUF PORT "sdram_dq[5]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[5]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[6]" SITE "T17";
IOBUF PORT "sdram_dq[6]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[6]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[7]" SITE "U20";
IOBUF PORT "sdram_dq[7]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[7]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[8]" SITE "E19";
IOBUF PORT "sdram_dq[8]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[8]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[9]" SITE "D20";
IOBUF PORT "sdram_dq[9]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[9]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[10]" SITE "D19";
IOBUF PORT "sdram_dq[10]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[10]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[11]" SITE "C20";
IOBUF PORT "sdram_dq[11]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[11]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[12]" SITE "E18";
IOBUF PORT "sdram_dq[12]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[12]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[13]" SITE "F18";
IOBUF PORT "sdram_dq[13]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[13]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[14]" SITE "J18";
IOBUF PORT "sdram_dq[14]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[14]" SLEWRATE=FAST;
LOCATE COMP "sdram_dq[15]" SITE "J17";
IOBUF PORT "sdram_dq[15]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dq[15]" SLEWRATE=FAST;
LOCATE COMP "sdram_we_n" SITE "T20";
IOBUF PORT "sdram_we_n" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_we_n" SLEWRATE=FAST;
LOCATE COMP "sdram_ras_n" SITE "R20";
IOBUF PORT "sdram_ras_n" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_ras_n" SLEWRATE=FAST;
LOCATE COMP "sdram_cas_n" SITE "T19";
IOBUF PORT "sdram_cas_n" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_cas_n" SLEWRATE=FAST;
LOCATE COMP "sdram_cs_n" SITE "P20";
IOBUF PORT "sdram_cs_n" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_cs_n" SLEWRATE=FAST;
LOCATE COMP "sdram_cke" SITE "F20";
IOBUF PORT "sdram_cke" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_cke" SLEWRATE=FAST;
LOCATE COMP "sdram_ba[0]" SITE "P19";
IOBUF PORT "sdram_ba[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_ba[0]" SLEWRATE=FAST;
LOCATE COMP "sdram_ba[1]" SITE "N20";
IOBUF PORT "sdram_ba[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_ba[1]" SLEWRATE=FAST;
LOCATE COMP "sdram_dm[0]" SITE "U19";
IOBUF PORT "sdram_dm[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dm[0]" SLEWRATE=FAST;
LOCATE COMP "sdram_dm[1]" SITE "E20";
IOBUF PORT "sdram_dm[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "sdram_dm[1]" SLEWRATE=FAST;
LOCATE COMP "gpdi_clk_p" SITE "A17";
IOBUF PORT "gpdi_clk_p" IO_TYPE=LVCMOS33D;
IOBUF PORT "gpdi_clk_p" DRIVE=4;
LOCATE COMP "gpdi_data0_p" SITE "A16";
IOBUF PORT "gpdi_data0_p" IO_TYPE=LVCMOS33D;
IOBUF PORT "gpdi_data0_p" DRIVE=4;
LOCATE COMP "gpdi_data1_p" SITE "A14";
IOBUF PORT "gpdi_data1_p" IO_TYPE=LVCMOS33D;
IOBUF PORT "gpdi_data1_p" DRIVE=4;
LOCATE COMP "gpdi_data2_p" SITE "A12";
IOBUF PORT "gpdi_data2_p" IO_TYPE=LVCMOS33D;
IOBUF PORT "gpdi_data2_p" DRIVE=4;
FREQUENCY PORT "clk25" 25.0 MHz;
LOCATE COMP "init_done" SITE "H3";
LOCATE COMP "init_error" SITE "E1";
Finally, you'll need a top.v
which instantiates a pll as follows:
module top(
input wire clk25,
input wire rst,
output reg uart_tx,
input wire uart_rx,
output wire [12:0] sdram_a,
output wire [1:0] sdram_ba,
output wire sdram_ras_n,
output wire sdram_cas_n,
output wire sdram_we_n,
output wire sdram_cs_n,
output wire [1:0] sdram_dm,
input wire [15:0] sdram_dq,
output wire sdram_cke,
output wire init_done,
output wire init_error,
output wire sdram_clock,
output wire user_rst
);
wire main_ecp5pll0_clkout0;
wire main_ecp5pll0_clkout1;
wire sys_ps_clk;
(* FREQUENCY_PIN_CLKI = "25.0", FREQUENCY_PIN_CLKOP = "50.0", FREQUENCY_PIN_CLKOS = "50.0", ICP_CURRENT = "6", LPF_RESISTOR = "16", MFG_ENABLE_FILTEROPAMP = "1", MFG_GMCREF_SEL = "2" *)
EHXPLLL #(
.CLKFB_DIV(5'd16),
.CLKI_DIV(1'd1),
.CLKOP_CPHASE(3'd7),
.CLKOP_DIV(4'd8),
.CLKOP_ENABLE("ENABLED"),
.CLKOP_FPHASE(1'd0),
.CLKOS2_CPHASE(1'd0),
.CLKOS2_DIV(1'd1),
.CLKOS2_ENABLE("ENABLED"),
.CLKOS2_FPHASE(1'd0),
.CLKOS_CPHASE(4'd9),
.CLKOS_DIV(4'd8),
.CLKOS_ENABLE("ENABLED"),
.CLKOS_FPHASE(1'd0),
.FEEDBK_PATH("INT_OS2")
) EHXPLLL (
.CLKI(clk25),
.RST(main_pll_reset),
.STDBY(main_pll_stdby),
.CLKOP(main_ecp5pll0_clkout0),
.CLKOS(main_ecp5pll0_clkout1),
.CLKOS2(builder_basesoc_ecp5pll0_ecp5pll),
.LOCK(builder_basesoc_ecp5pll0_locked)
);
sdram_controller sub
( .uart_tx(uart_tx)
, .uart_rx(uart_rx)
, .clk(main_ecp5pll0_clkout0)
, .rst(rst)
, .sdram_a(sdram_a)
, .sdram_ba(sdram_ba)
, .sdram_ras_n(sdram_ras_n)
, .sdram_cas_n(sdram_cas_n)
, .sdram_we_n(sdram_we_n)
, .sdram_cs_n(sdram_cs_n)
, .sdram_dm(sdram_dm)
, .sdram_dq(sdram_dq)
, .sdram_cke(sdram_cke)
, .init_done(init_done)
, .init_error(init_error)
, .user_clk(user_clk)
, .user_rst(user_rst)
);
assign sys_ps_clk = main_ecp5pll0_clkout1;
ODDRX1F ODDRX1F(
.D0(1'd1),
.D1(1'd0),
.SCLK(sys_ps_clk),
.Q(sdram_clock)
);
endmodule
Perhaps there are better ways to achieve this, but at least this gets it working.
Hi @TheZoq2,
sorry for the delay and good to know you've got it running. The approach used seems fine. Thanks for sharing your project, this will be useful for users willing do the the same. I'll also probably add a link to it in the wiki.
Cool! Thanks for your work on this project, it is really useful!
I'm trying to generate a litedram core for my ulx3s, but am having some trouble. As a first step, I want to see if I can get it working in the simplest configuration possible, so I removed all
user_ports
from the ulx3s example, modified the device from -45F to -85F as that's the device I have, and I changed the CPU to vexriscv instead of serv, to make the init process faster. Finally, I changed the sys_clk_freq to 25e6 instead of 50e6, which is what the ulx3s clock is natively.This is my modified yml
I built it with
The core boots fine, but sdram_init fails
I also tried changing the sdram_module to
IS42S16160
as that is what is included on pre-2022 ulx3s boards https://github.com/emard/ulx3s/blob/master/doc/MANUAL.md#board-versionsFinally I use this lpf file
in which I copied most configurations from https://github.com/emard/ulx3s/blob/master/doc/constraints/ulx3s_v20.lpf
The only significant changes I made to it was to remove
sdram_clk
and replace it byuser_clk
(under the assumption thatuser_clk
only forwards thesys_clk
. I also renamedsdram_dqm
from the original file withsdram_dm
Am I missing something here that makes this not work?