newStep.v
This commit is contained in:
330
RTL/SDRAM/muchtoremember.v
Normal file
330
RTL/SDRAM/muchtoremember.v
Normal file
@@ -0,0 +1,330 @@
|
||||
|
||||
// SDRAM interface to AS4C32M16SB-7TCN
|
||||
// 512 Mbit Single-Data-Rate SDRAM, 32Mx16 (8M x 16 x 4 Banks)
|
||||
|
||||
// Matthias Koch, January 2022
|
||||
|
||||
// With a lot of inspiration from Mike Field, Hamsterworks:
|
||||
|
||||
// https://web.archive.org/web/20190215130043/http://hamsterworks.co.nz/mediawiki/index.php/Simple_SDRAM_Controller
|
||||
// https://web.archive.org/web/20190215130043/http://hamsterworks.co.nz/mediawiki/index.php/File:Verilog_Memory_controller_v0.1.zip
|
||||
|
||||
// Note: You may need to change all values marked with *** when changing clock frequency. This is for 40 MHz.
|
||||
|
||||
module muchtoremember (
|
||||
|
||||
// Interface to SDRAM chip, fully registered
|
||||
|
||||
output sd_clk, // Clock for SDRAM chip
|
||||
output reg sd_cke, // Clock enabled
|
||||
inout [15:0] sd_d, // Bidirectional data lines to/from SDRAM
|
||||
output reg [12:0] sd_addr, // Address bus, multiplexed, 13 bits
|
||||
output reg [1:0] sd_ba, // Bank select wires for 4 banks
|
||||
output reg [1:0] sd_dqm, // Byte mask
|
||||
output reg sd_cs, // Chip select
|
||||
output reg sd_we, // Write enable
|
||||
output reg sd_ras, // Row address select
|
||||
output reg sd_cas, // Columns address select
|
||||
|
||||
// Interface to processor
|
||||
|
||||
input clk,
|
||||
input resetn,
|
||||
input [3:0] wmask,
|
||||
input rd,
|
||||
input [25:0] addr,
|
||||
input [31:0] din,
|
||||
output reg [31:0] dout,
|
||||
output reg busy
|
||||
);
|
||||
|
||||
parameter sdram_startup_cycles = 10100; // *** -- 100us, plus a little more, @ 100MHz
|
||||
parameter sdram_refresh_cycles = 195; // *** The refresh operation must be performed 8192 times within 64ms. --> One refresh every 7.8125 us.
|
||||
// With a minimum clock of 25 MHz, this results in one refresh every 7.8125e-6 * 25e6 = 195 cycles.
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// -- Connections and buffer primitives
|
||||
// ----------------------------------------------------------
|
||||
|
||||
assign sd_clk = ~clk; // Supply memory chip with a clock.
|
||||
|
||||
wire [15:0] sd_data_in; // Bidirectional data from SDRAM
|
||||
reg [15:0] sd_data_out; // Bidirectional data to SDRAM
|
||||
reg sd_data_drive; // High: FPGA controls wires Low: SDRAM controls wires
|
||||
|
||||
|
||||
`ifdef __ICARUS__
|
||||
|
||||
reg [15:0] sd_data_in_buffered;
|
||||
assign sd_d = sd_data_drive ? sd_data_out : 16'bz;
|
||||
always @(posedge clk) sd_data_in_buffered <= sd_d;
|
||||
assign sd_data_in = sd_data_in_buffered;
|
||||
|
||||
`else
|
||||
|
||||
wire [15:0] sd_data_in_unbuffered; // To connect primitives internally
|
||||
|
||||
TRELLIS_IO #(.DIR("BIDIR"))
|
||||
sdio_tristate[15:0] (
|
||||
.B(sd_d),
|
||||
.I(sd_data_out),
|
||||
.O(sd_data_in_unbuffered),
|
||||
.T(!sd_data_drive)
|
||||
);
|
||||
|
||||
// Registering the input is important for stability and delays data arrival by one clock cycle.
|
||||
IFS1P3BX dbi_ff[15:0] (.D(sd_data_in_unbuffered), .Q(sd_data_in), .SCLK(clk), .PD({16{sd_data_drive}}));
|
||||
|
||||
`endif
|
||||
// ----------------------------------------------------------
|
||||
// -- Configuration to initialise the SDRAM chip
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// Taken from https://github.com/rxrbln/picorv32/blob/master/picosoc/sdram.v
|
||||
|
||||
localparam NO_WRITE_BURST = 1'b0; // 0=write burst enabled, 1=only single access write
|
||||
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
|
||||
localparam CAS_LATENCY = 3'd2; // 2 or 3 cycles allowed
|
||||
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
|
||||
localparam BURST_LENGTH = 3'b001; // 000=1, 001=2, 010=4, 011=8
|
||||
|
||||
localparam MODE = {3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// -- All possible commands for the SDRAM chip
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// CS, RAS, CAS, WE
|
||||
localparam CMD_INHIBIT = 4'b1111;
|
||||
|
||||
localparam CMD_NOP = 4'b0111;
|
||||
localparam CMD_BURST_TERMINATE = 4'b0110;
|
||||
localparam CMD_READ = 4'b0101;
|
||||
localparam CMD_WRITE = 4'b0100;
|
||||
localparam CMD_ACTIVE = 4'b0011;
|
||||
localparam CMD_PRECHARGE = 4'b0010;
|
||||
localparam CMD_AUTO_REFRESH = 4'b0001;
|
||||
localparam CMD_LOAD_MODE = 4'b0000;
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// -- States of the SDRAM controller
|
||||
// ----------------------------------------------------------
|
||||
|
||||
localparam s_init_bit = 0; localparam s_init = 1 << s_init_bit ;
|
||||
localparam s_idle_bit = 1; localparam s_idle = 1 << s_idle_bit ;
|
||||
localparam s_activate_bit = 2; localparam s_activate = 1 << s_activate_bit ;
|
||||
localparam s_read_1_bit = 3; localparam s_read_1 = 1 << s_read_1_bit ;
|
||||
localparam s_read_2_bit = 4; localparam s_read_2 = 1 << s_read_2_bit ;
|
||||
localparam s_read_3_bit = 5; localparam s_read_3 = 1 << s_read_3_bit ;
|
||||
localparam s_read_4_bit = 6; localparam s_read_4 = 1 << s_read_4_bit ;
|
||||
localparam s_read_5_bit = 7; localparam s_read_5 = 1 << s_read_5_bit ;
|
||||
localparam s_write_1_bit = 8; localparam s_write_1 = 1 << s_write_1_bit ;
|
||||
localparam s_write_2_bit = 9; localparam s_write_2 = 1 << s_write_2_bit ;
|
||||
|
||||
localparam s_idle_in_6_bit = 10; localparam s_idle_in_6 = 1 << s_idle_in_6_bit ;
|
||||
localparam s_idle_in_5_bit = 11; localparam s_idle_in_5 = 1 << s_idle_in_5_bit ;
|
||||
localparam s_idle_in_4_bit = 12; localparam s_idle_in_4 = 1 << s_idle_in_4_bit ;
|
||||
localparam s_idle_in_3_bit = 13; localparam s_idle_in_3 = 1 << s_idle_in_3_bit ;
|
||||
localparam s_idle_in_2_bit = 14; localparam s_idle_in_2 = 1 << s_idle_in_2_bit ;
|
||||
localparam s_idle_in_1_bit = 15; localparam s_idle_in_1 = 1 << s_idle_in_1_bit ;
|
||||
|
||||
(* onehot *)
|
||||
reg [15:0] state = s_init;
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// -- Access control wires
|
||||
// ----------------------------------------------------------
|
||||
|
||||
reg [14:0] reset_counter = sdram_startup_cycles;
|
||||
reg [7:0] refresh_counter = 0;
|
||||
reg refresh_pending = 1;
|
||||
reg rd_sticky = 0;
|
||||
reg [3:0] wmask_sticky = 4'b0000;
|
||||
|
||||
wire stillatwork = ~(state[s_read_5_bit] | state[s_write_2_bit]);
|
||||
wire [8:0] refresh_counterN = refresh_counter - 1;
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// -- The memory controller
|
||||
// ----------------------------------------------------------
|
||||
|
||||
always @(posedge clk)
|
||||
if(!resetn) begin
|
||||
state <= s_init;
|
||||
reset_counter <= sdram_startup_cycles; // Counts backwards to zero
|
||||
busy <= 0; // Technically, we are busy with initialisation, but there are no ongoing read or write requests
|
||||
rd_sticky <= 0;
|
||||
wmask_sticky <= 4'b0000;
|
||||
sd_cke <= 0;
|
||||
end else begin
|
||||
|
||||
// FemtoRV32 pulses read and write lines high for exactly one clock cycle.
|
||||
// Address and data lines keep stable until busy is released.
|
||||
// Therefore: Take note of the requested read or write, and assert busy flag immediately.
|
||||
|
||||
busy <= ((|wmask) | rd) | (busy & stillatwork );
|
||||
rd_sticky <= rd | (rd_sticky & stillatwork );
|
||||
wmask_sticky <= wmask | (wmask_sticky & {4{stillatwork}} );
|
||||
|
||||
// Schedule refreshes regularly
|
||||
refresh_counter <= refresh_counterN[8] ? sdram_refresh_cycles : refresh_counterN[7:0];
|
||||
refresh_pending <= (refresh_pending & ~state[s_idle_bit]) | refresh_counterN[8];
|
||||
|
||||
(* parallel_case *)
|
||||
case(1'b1)
|
||||
|
||||
// Processor can already request the first read or write here, but has to wait then:
|
||||
|
||||
state[s_init_bit]: begin
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//-- This is the initial startup state, where we wait for at least 100us
|
||||
//-- before starting the start sequence
|
||||
//--
|
||||
//-- The initialisation is sequence is
|
||||
//-- * de-assert SDRAM_CKE
|
||||
//-- * 100us wait,
|
||||
//-- * assert SDRAM_CKE
|
||||
//-- * wait at least one cycle,
|
||||
//-- * PRECHARGE
|
||||
//-- * wait 2 cycles
|
||||
//-- * REFRESH,
|
||||
//-- * tREF wait
|
||||
//-- * REFRESH,
|
||||
//-- * tREF wait
|
||||
//-- * LOAD_MODE_REG
|
||||
//-- * 2 cycles wait
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
sd_ba <= 2'b00; // Reserved for future use in mode configuration
|
||||
sd_dqm <= 2'b11; // Data bus in High-Z state
|
||||
sd_data_drive <= 0; // Do not drive the data bus now
|
||||
|
||||
case (reset_counter) // Counts from a large value down to zero
|
||||
|
||||
33: begin sd_cke <= 1; end
|
||||
|
||||
// Ensure all rows are closed
|
||||
31: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_PRECHARGE; sd_addr <= 13'b0010000000000; end
|
||||
|
||||
// These refreshes need to be at least tRFC (63ns) apart
|
||||
23: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_AUTO_REFRESH; end
|
||||
15: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_AUTO_REFRESH; end
|
||||
|
||||
// Now load the mode register
|
||||
7: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_LOAD_MODE; sd_addr <= MODE; end
|
||||
|
||||
default: {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
|
||||
endcase
|
||||
|
||||
reset_counter <= reset_counter - 1;
|
||||
if (reset_counter == 0) state <= s_idle;
|
||||
end
|
||||
|
||||
// New read or write requests from the processor may arrive in these states:
|
||||
|
||||
//-----------------------------------------------------
|
||||
//-- Additional NOPs to meet timing requirements
|
||||
//-----------------------------------------------------
|
||||
|
||||
state[s_idle_in_6_bit]: begin state <= s_idle_in_5; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
state[s_idle_in_5_bit]: begin state <= s_idle_in_4; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
state[s_idle_in_4_bit]: begin state <= s_idle_in_3; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
state[s_idle_in_3_bit]: begin state <= s_idle_in_2; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
state[s_idle_in_2_bit]: begin state <= s_idle_in_1; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
state[s_idle_in_1_bit]: begin state <= s_idle; {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
|
||||
|
||||
// Refresh cycle needs tRFC (63ns), so 6 idle cycles are needed @ 100MHz
|
||||
|
||||
//-----------------------------------------------------
|
||||
//-- Dispatch all possible actions while idling (NOP)
|
||||
//-----------------------------------------------------
|
||||
|
||||
state[s_idle_bit]: begin
|
||||
sd_ba <= addr[23:22]; // Bank select, 2 bits
|
||||
sd_addr <= {addr[25:24], addr[21:11]} ; // RA0-RA12: 8192 Row address
|
||||
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= refresh_pending ? CMD_AUTO_REFRESH :
|
||||
(|wmask_sticky) | rd_sticky ? CMD_ACTIVE :
|
||||
CMD_NOP;
|
||||
|
||||
state <= refresh_pending ? s_idle_in_2 : // *** Experimental result: Direct transition to s_idle does not work @ 40 MHz, s_idle_in_1 is unstable, sd_idle_in_2 is fine.
|
||||
(|wmask_sticky) | rd_sticky ? s_activate :
|
||||
s_idle;
|
||||
end
|
||||
|
||||
// Busy flag is set while state machine is in the following states:
|
||||
|
||||
//-----------------------------------------------------
|
||||
//-- Opening the row ready for reads or writes
|
||||
//-----------------------------------------------------
|
||||
|
||||
state[s_activate_bit]: begin
|
||||
sd_data_drive <= ~rd_sticky; // Drive or release bus early, before the SDRAM chip takes over to drive these lines
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
|
||||
state <= rd_sticky ? s_read_1 : s_write_1;
|
||||
end
|
||||
|
||||
// RAS-to-CAS delay, also necessary for precharge, used in this state machine: 2 cycles.
|
||||
// Specification of AS4C32M16SB-7TCN: 21 ns --> Good for 1/(21e-9 / 2) = 95.23 MHz
|
||||
|
||||
//-----------------------------------------------------
|
||||
//-- Processing the read transaction
|
||||
//-----------------------------------------------------
|
||||
|
||||
state[s_read_1_bit]: begin
|
||||
sd_dqm <= 2'b00; // SDRAM chip shall drive the bus lines
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= CMD_READ;
|
||||
sd_addr <= {3'b001, addr[10:2], 1'b0}; // Bit 10: Auto-precharge. CA0-CA9: 1024 Column address.
|
||||
state <= s_read_2;
|
||||
end
|
||||
|
||||
state[s_read_2_bit]: begin
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
|
||||
state <= s_read_3;
|
||||
end
|
||||
|
||||
state[s_read_3_bit]: state <= s_read_4;
|
||||
|
||||
|
||||
state[s_read_4_bit]: begin
|
||||
dout[15:0] <= sd_data_in;
|
||||
state <= s_read_5;
|
||||
end
|
||||
|
||||
// Busy is cleared when reaching this state, fulfilling the request:
|
||||
|
||||
state[s_read_5_bit]: begin
|
||||
dout[31:16] <= sd_data_in;
|
||||
state <= s_idle; // *** Experimental result: Direct transition to s_idle is fine @ 40 MHz
|
||||
end
|
||||
|
||||
// Precharge (which is automatic here) needs 21 ns, therefore 2 idle cycles need to be inserted
|
||||
|
||||
//-----------------------------------------------------
|
||||
// -- Processing the write transaction
|
||||
//-----------------------------------------------------
|
||||
|
||||
state[s_write_1_bit]: begin
|
||||
sd_addr <= {3'b001, addr[10:2], 1'b0}; // Bit 10: Auto-precharge. CA0-CA9: 1024 Column address.
|
||||
sd_data_out <= din[15:0];
|
||||
sd_dqm <= ~wmask_sticky[1:0];
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= CMD_WRITE;
|
||||
state <= s_write_2;
|
||||
end
|
||||
|
||||
// Busy is cleared when reaching this state, fulfilling the request:
|
||||
|
||||
state[s_write_2_bit]: begin
|
||||
sd_data_out <= din[31:16];
|
||||
sd_dqm <= ~wmask_sticky[3:2];
|
||||
{sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
|
||||
state <= s_idle_in_2; // *** Experimental result: s_idle_in_1 does not work @ 40 MHz, s_idle_in_2 is fine.
|
||||
end
|
||||
|
||||
// Write needs 14 ns internally, then Precharge needs 21 ns, therefore 3 idle cycles need to be inserted
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
1072
RTL/SDRAM/simulation/mt48lc16m16a2.v
Normal file
1072
RTL/SDRAM/simulation/mt48lc16m16a2.v
Normal file
File diff suppressed because it is too large
Load Diff
6
RTL/SDRAM/simulation/run-icarus
Executable file
6
RTL/SDRAM/simulation/run-icarus
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
iverilog -o test_sdram.vvp -s test_sdram test_sdram.v ../muchtoremember.v mt48lc16m16a2.v
|
||||
vvp *.vvp
|
||||
|
||||
|
||||
116
RTL/SDRAM/simulation/test_sdram.v
Normal file
116
RTL/SDRAM/simulation/test_sdram.v
Normal file
@@ -0,0 +1,116 @@
|
||||
`timescale 1ns/100ps // 1 ns time unit, 100 ps resolution
|
||||
`default_nettype none // Makes it easier to detect typos !
|
||||
|
||||
module test_sdram;
|
||||
reg clk;
|
||||
always #12.5 clk = !clk;
|
||||
|
||||
reg resetq = 0;
|
||||
|
||||
/***************************************************************************/
|
||||
// SD-RAM-Controller
|
||||
/***************************************************************************/
|
||||
|
||||
wire [31:0] sdram_rdata;
|
||||
wire sdram_busy;
|
||||
|
||||
reg [3:0] sdram_wmask = 4'b0000;
|
||||
reg sdram_rd = 0;
|
||||
|
||||
muchtoremember sdram(
|
||||
// Physical interface
|
||||
.sd_d(sdram_d),
|
||||
.sd_addr(sdram_a),
|
||||
.sd_dqm(sdram_dqm),
|
||||
.sd_cs(sdram_csn),
|
||||
.sd_ba(sdram_ba),
|
||||
.sd_we(sdram_wen),
|
||||
.sd_ras(sdram_rasn),
|
||||
.sd_cas(sdram_casn),
|
||||
.sd_clk(sdram_clk),
|
||||
.sd_cke(sdram_cke),
|
||||
|
||||
// Internal bus interface
|
||||
.clk(clk),
|
||||
.resetn(resetq),
|
||||
.addr(mem_address[25:0]),
|
||||
.wmask(sdram_wmask),
|
||||
.rd(sdram_rd),
|
||||
.din(mem_wdata),
|
||||
.dout(sdram_rdata),
|
||||
.busy(sdram_busy)
|
||||
);
|
||||
|
||||
wire [31:0] mem_address = 0;
|
||||
wire [31:0] mem_wdata = 32'h00030004;
|
||||
|
||||
/***************************************************************************/
|
||||
// 64 MB SD-RAM
|
||||
/***************************************************************************/
|
||||
|
||||
wire sdram_csn; // chip select
|
||||
wire sdram_clk; // clock to SDRAM
|
||||
wire sdram_cke; // clock enable to SDRAM
|
||||
wire sdram_rasn; // SDRAM RAS
|
||||
wire sdram_casn; // SDRAM CAS
|
||||
wire sdram_wen; // SDRAM write-enable
|
||||
wire [12:0] sdram_a; // SDRAM address bus
|
||||
wire [1:0] sdram_ba; // SDRAM bank-address
|
||||
wire [1:0] sdram_dqm; // byte select
|
||||
wire [15:0] sdram_d;
|
||||
|
||||
mt48lc16m16a2 memory(
|
||||
.Dq(sdram_d),
|
||||
.Addr(sdram_a),
|
||||
.Ba(sdram_ba),
|
||||
.Clk(sdram_clk),
|
||||
.Cke(sdram_cke),
|
||||
.Cs_n(sdram_csn),
|
||||
.Ras_n(sdram_rasn),
|
||||
.Cas_n(sdram_casn),
|
||||
.We_n(sdram_wen),
|
||||
.Dqm(sdram_dqm)
|
||||
);
|
||||
|
||||
/***************************************************************************/
|
||||
// Test sequence
|
||||
/***************************************************************************/
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
$dumpfile("sdram.vcd"); // create a VCD waveform dump
|
||||
$dumpvars(0, test_sdram); // dump variable changes in the testbench
|
||||
// and all modules under it
|
||||
|
||||
clk = 0;
|
||||
resetq = 0;
|
||||
@(negedge clk);
|
||||
resetq = 1;
|
||||
|
||||
for (i = 0; i < 11000; i = i + 1) begin
|
||||
@(negedge clk);
|
||||
end
|
||||
|
||||
$monitor("t=%d: sdram_d = %8h Busy %b sdram_rdata %8h", $time, sdram_d, sdram_busy, sdram_rdata);
|
||||
|
||||
$display(" --- Write access ---");
|
||||
sdram_wmask = 15;
|
||||
@(negedge clk);
|
||||
sdram_wmask = 0;
|
||||
|
||||
for (i = 0; i < 64; i = i + 1) begin
|
||||
@(negedge clk);
|
||||
end
|
||||
|
||||
$display(" --- Read access ---");
|
||||
sdram_rd = 1;
|
||||
@(negedge clk);
|
||||
sdram_rd = 0;
|
||||
|
||||
for (i = 0; i < 64; i = i + 1) begin
|
||||
@(negedge clk);
|
||||
end
|
||||
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
||||
6
RTL/SDRAM/simulation/tidyup-icarus
Executable file
6
RTL/SDRAM/simulation/tidyup-icarus
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f *.vvp
|
||||
rm -f *.vcd
|
||||
|
||||
|
||||
Reference in New Issue
Block a user