1
0
mirror of git://projects.qi-hardware.com/nn-usb-fpga.git synced 2025-01-11 01:00:15 +02:00
nn-usb-fpga/lm32/logic/sakc/rtl/wb_ddr/wb_ddr.v
2010-05-25 21:49:58 -05:00

622 lines
16 KiB
Verilog

//----------------------------------------------------------------------------
// Wishbone DDR Controller
//
// (c) Joerg Bornschein (<jb@capsec.org>)
//----------------------------------------------------------------------------
`include "ddr_include.v"
module wb_ddr
#(
parameter clk_freq = 100000000,
parameter clk_multiply = 12,
parameter clk_divide = 5,
parameter phase_shift = 0,
parameter wait200_init = 26
) (
input clk,
input reset,
// DDR ports
output [2:0] ddr_clk,
output [2:0] ddr_clk_n,
input ddr_clk_fb,
output ddr_ras_n,
output ddr_cas_n,
output ddr_we_n,
output [1:0] ddr_cke,
output [1:0] ddr_cs_n,
output [ `A_RNG] ddr_a,
output [ `BA_RNG] ddr_ba,
inout [ `DQ_RNG] ddr_dq,
inout [`DQS_RNG] ddr_dqs,
output [ `DM_RNG] ddr_dm,
// Wishbone Slave Interface
input [`WB_ADR_RNG] wb_adr_i,
input [`WB_DAT_RNG] wb_dat_i,
output reg [`WB_DAT_RNG] wb_dat_o,
input [`WB_SEL_RNG] wb_sel_i,
input wb_cyc_i,
input wb_stb_i,
input wb_we_i,
output reg wb_ack_o,
// XXX Temporary DCM control input XXX
output ps_ready,
input ps_up,
input ps_down,
// XXX probe wires XXX
output probe_clk,
input [7:0] probe_sel,
output reg [7:0] probe
);
//----------------------------------------------------------------------------
// Wishbone handling
//----------------------------------------------------------------------------
wire wb_rd = wb_stb_i & wb_cyc_i & ~wb_we_i;
wire wb_wr = wb_stb_i & wb_cyc_i & wb_we_i;
wire [`WB_WORD_RNG] wb_adr_word = wb_adr_i[`WB_WORD_RNG]; // word in bufferline
wire [`WB_SET_RNG] wb_adr_set = wb_adr_i[`WB_SET_RNG]; // index into wayX_ram
wire [`WB_TAG_RNG] wb_adr_tag = wb_adr_i[`WB_TAG_RNG]; // more significant bits
//----------------------------------------------------------------------------
// TAG RAM (2-way set assioziative)
//----------------------------------------------------------------------------
wire [`TAG_LINE_RNG] tag_load;
wire [`TAG_LINE_RNG] tag_store;
wire tag_we;
wire [`WB_TAG_RNG] tag_load_set0 = tag_load[`TAG_LINE_TAG0_RNG];
wire [`WB_TAG_RNG] tag_load_set1 = tag_load[`TAG_LINE_TAG1_RNG];
wire tag_load_dirty0 = tag_load[`TAG_LINE_DIRTY0_RNG];
wire tag_load_dirty1 = tag_load[`TAG_LINE_DIRTY1_RNG];
wire tag_load_lru = tag_load[`TAG_LINE_LRU_RNG];
reg [`WB_TAG_RNG] tag_store_set0;
reg [`WB_TAG_RNG] tag_store_set1;
reg tag_store_dirty0;
reg tag_store_dirty1;
reg tag_store_lru;
assign tag_store[`TAG_LINE_TAG0_RNG] = tag_store_set0;
assign tag_store[`TAG_LINE_TAG1_RNG] = tag_store_set1;
assign tag_store[`TAG_LINE_DIRTY0_RNG] = tag_store_dirty0;
assign tag_store[`TAG_LINE_DIRTY1_RNG] = tag_store_dirty1;
assign tag_store[`TAG_LINE_LRU_RNG] = tag_store_lru;
wire [`WB_SET_RNG] ls_tag_adr;
wire [`TAG_LINE_RNG] ls_tag_load;
reg [`TAG_LINE_RNG] ls_tag_store;
reg ls_tag_we;
dpram #(
.adr_width( 7 ),
.dat_width( 33 )
) tag_ram (
.clk ( clk ),
//
.adr0( wb_adr_set ),
.dout0( tag_load ),
.din0( tag_store ),
.we0( tag_we ),
//
.adr1( ls_tag_adr ),
.dout1( ls_tag_load ),
.din1( ls_tag_store ),
.we1( ls_tag_we )
);
wire tag_load_match0 = (tag_load_set0 == wb_adr_tag);
wire tag_load_match1 = (tag_load_set1 == wb_adr_tag);
wire tag_load_match = tag_load_match0 | tag_load_match1;
//----------------------------------------------------------------------------
// Buffer cache ram (2 ways)
//----------------------------------------------------------------------------
wire [8:0] wayX_adr = { wb_adr_set, wb_adr_word };
wire [`WAY_LINE_RNG] way0_load, way1_load;
wire [`WAY_LINE_RNG] wayX_store;
wire [31:0] way0_load_dat = way0_load[`WAY_DAT_RNG];
wire [31:0] way1_load_dat = way1_load[`WAY_DAT_RNG];
wire [3:0] way0_load_valid = way0_load[`WAY_VALID_RNG];
wire [3:0] way1_load_valid = way1_load[`WAY_VALID_RNG];
wire way0_we;
wire way1_we;
reg [31:0] wayX_store_dat;
reg [3:0] wayX_store_valid;
assign wayX_store[`WAY_DAT_RNG] = wayX_store_dat;
assign wayX_store[`WAY_VALID_RNG] = wayX_store_valid;
wire [8:0] ls_wayX_adr;
wire [`WAY_LINE_RNG] ls_way0_load;
wire [`WAY_LINE_RNG] ls_way1_load;
wire [`WAY_LINE_RNG] ls_wayX_store;
wire ls_way0_we;
wire ls_way1_we;
reg ls_wayX_we;
wire way0_sel_valid = ( (~way0_load_valid & wb_sel_i) == 'b0);
wire way1_sel_valid = ( (~way1_load_valid & wb_sel_i) == 'b0);
wire wayX_sel_valid = (tag_load_match0) ? way0_sel_valid : way1_sel_valid;
// synthesis attribute ram_style of way0_ram is block
dpram #(
.adr_width( 9 ),
.dat_width( 36 )
) way0_ram (
.clk( clk ),
//
.adr0( wayX_adr ),
.dout0( way0_load ),
.din0( wayX_store ),
.we0( way0_we ),
//
.adr1( ls_wayX_adr ),
.dout1( ls_way0_load ),
.we1( ls_way0_we ),
.din1( ls_wayX_store )
);
// synthesis attribute ram_style of way1_ram is block
dpram #(
.adr_width( 9 ),
.dat_width( 36 )
) way1_ram (
.clk( clk ),
//
.adr0( wayX_adr ),
.dout0( way1_load ),
.din0( wayX_store ),
.we0( way1_we ),
//
.adr1( ls_wayX_adr ),
.dout1( ls_way1_load ),
.we1( ls_way1_we ),
.din1( ls_wayX_store )
);
//----------------------------------------------------------------------------
// Write/update buffer cache from wishbone side
//----------------------------------------------------------------------------
wire store_to_way0 = tag_load_lru & ~tag_load_dirty0; // store new data into way0? XXX spill_done XXX
wire store_to_way1 = ~tag_load_lru & ~tag_load_dirty1; // store new data into way1? XXX spill_done XXX
wire store_to_way = store_to_way0 | store_to_way1;
reg update_lru0; //
reg update_lru1;
reg update_way0; //
reg update_way1;
assign way0_we = update_way0;
assign way1_we = update_way1;
assign tag_we = way0_we | way1_we | update_lru0 | update_lru1;
//----------------------------------------------------------------------------
// MUX wayX_store input
//----------------------------------------------------------------------------
integer i;
always @(*)
begin
/*
for(i=0; i<4; i=i+1) begin
if (wb_sel_i[i]) begin
wayX_store_dat[8*i+7:8*i] = wb_dat_i[8*i+7:8*i];
wayX_store_valid[i] = 1;
end else if (update_way0) begin
wayX_store_dat[8*i+7:8*i] = way0_load_dat[8*i+7:8*i];
wayX_store_valid[i] = way0_load_valid[i];
end else begin
wayX_store_dat[8*i+7:8*i] = way1_load_dat[8*i+7:8*i];
wayX_store_valid[i] = way1_load_valid[i];
end
end
*/
if (wb_sel_i[0]) begin
wayX_store_dat[8*0+7:8*0] = wb_dat_i[8*0+7:8*0];
wayX_store_valid[0] = 1;
end else if (update_way0) begin
wayX_store_dat[8*0+7:8*0] = way0_load_dat[8*0+7:8*0];
wayX_store_valid[0] = way0_load_valid[0];
end else begin
wayX_store_dat[8*0+7:8*0] = way1_load_dat[8*0+7:8*0];
wayX_store_valid[0] = way1_load_valid[0];
end
if (wb_sel_i[1]) begin
wayX_store_dat[8*1+7:8*1] = wb_dat_i[8*1+7:8*1];
wayX_store_valid[1] = 1;
end else if (update_way0) begin
wayX_store_dat[8*1+7:8*1] = way0_load_dat[8*1+7:8*1];
wayX_store_valid[1] = way0_load_valid[1];
end else begin
wayX_store_dat[8*1+7:8*1] = way1_load_dat[8*1+7:8*1];
wayX_store_valid[1] = way1_load_valid[1];
end
if (wb_sel_i[2]) begin
wayX_store_dat[8*2+7:8*2] = wb_dat_i[8*2+7:8*2];
wayX_store_valid[2] = 1;
end else if (update_way0) begin
wayX_store_dat[8*2+7:8*2] = way0_load_dat[8*2+7:8*2];
wayX_store_valid[2] = way0_load_valid[2];
end else begin
wayX_store_dat[8*2+7:8*2] = way1_load_dat[8*2+7:8*2];
wayX_store_valid[2] = way1_load_valid[2];
end
if (wb_sel_i[3]) begin
wayX_store_dat[8*3+7:8*3] = wb_dat_i[8*3+7:8*3];
wayX_store_valid[3] = 1;
end else if (update_way0) begin
wayX_store_dat[8*3+7:8*3] = way0_load_dat[8*3+7:8*3];
wayX_store_valid[3] = way0_load_valid[3];
end else begin
wayX_store_dat[8*3+7:8*3] = way1_load_dat[8*3+7:8*3];
wayX_store_valid[3] = way1_load_valid[3];
end
end
always @(*)
begin
if (update_way0) begin
tag_store_set0 = wb_adr_tag;
tag_store_dirty0 = 1;
end else begin
tag_store_set0 = tag_load_set0;
tag_store_dirty0 = tag_load_dirty0;
end
if (update_way1) begin
tag_store_set1 = wb_adr_tag;
tag_store_dirty1 = 1;
end else begin
tag_store_set1 = tag_load_set1;
tag_store_dirty1 = tag_load_dirty1;
end
if (update_lru0)
tag_store_lru = 0;
else if (update_lru1)
tag_store_lru = 1;
else
tag_store_lru = tag_load_lru;
end
//----------------------------------------------------------------------------
// Wishbone FSM
//----------------------------------------------------------------------------
reg ls_fill;
reg ls_spill;
reg ls_way;
wire ls_busy;
reg [`WB_TAG_RNG] ls_adr_tag;
reg [`WB_SET_RNG] ls_adr_set;
reg [`WB_WORD_RNG] ls_adr_word;
reg [2:0] state;
parameter s_idle = 0;
parameter s_read = 1;
parameter s_rspill = 2;
parameter s_rfill = 3;
parameter s_write = 4;
parameter s_wspill = 5;
// Syncronous part of FSM
always @(posedge clk)
begin
if (reset) begin
state <= s_idle;
ls_spill <= 0;
ls_fill <= 0;
ls_way <= 0;
end else begin
ls_fill <= 0;
ls_spill <= 0;
case (state)
s_idle: begin
if (wb_rd)
state <= s_read;
if (wb_wr)
state <= s_write;
end
s_read: begin
if ((tag_load_match0 & way0_sel_valid) | (tag_load_match1 & way1_sel_valid)) begin
state <= s_idle;
end else if (store_to_way & ~ls_busy) begin
state <= s_rfill;
ls_fill <= 1;
ls_way <= ~tag_load_lru;
ls_adr_tag <= wb_adr_tag;
ls_adr_set <= wb_adr_set;
end else if (~ls_busy) begin
state <= s_rspill;
ls_spill <= 1;
ls_way <= ~tag_load_lru;
ls_adr_set <= wb_adr_set;
if (tag_load_lru == 1)
ls_adr_tag <= tag_load_set0;
else
ls_adr_tag <= tag_load_set1;
end
end
s_rspill: begin
if (~ls_busy) begin
state <= s_rfill;
ls_fill <= 1;
ls_way <= ~tag_load_lru;
ls_adr_tag <= wb_adr_tag;
ls_adr_set <= wb_adr_set;
end
end
s_rfill: begin
if (tag_load_match & wayX_sel_valid)
state <= s_idle;
end
s_write: begin
if (tag_load_match | store_to_way) begin
state <= s_idle;
end else if (~ls_busy) begin
state <= s_wspill;
ls_spill <= 1;
ls_way <= ~tag_load_lru;
ls_adr_set <= wb_adr_set;
if (tag_load_lru == 1)
ls_adr_tag <= tag_load_set0;
else
ls_adr_tag <= tag_load_set1;
end
end
s_wspill: begin
if (tag_load_match | store_to_way) begin
state <= s_idle;
end
end
default:
state <= s_idle;
endcase
end
end
// Asyncronous part of FSM
always @(*)
begin
update_lru0 <= 0;
update_lru1 <= 0;
update_way0 <= 0;
update_way1 <= 0;
wb_dat_o <= 0;
wb_ack_o <= 0;
case (state)
s_idle: begin end
s_read: begin
if (tag_load_match0 & way0_sel_valid) begin
update_lru0 <= 1;
wb_dat_o <= way0_load_dat;
wb_ack_o <= 1;
end else if (tag_load_match1 & way1_sel_valid) begin
update_lru1 <= 1;
wb_dat_o <= way1_load_dat;
wb_ack_o <= 1;
end
end
s_write: begin
if (tag_load_match0 | store_to_way0) begin
update_lru0 <= 1;
update_way0 <= 1;
wb_ack_o <= 1;
end else if (tag_load_match1 | store_to_way1) begin
update_lru1 <= 1;
update_way1 <= 1;
wb_ack_o <= 1;
end
end
endcase
end
//----------------------------------------------------------------------------
// DDR Controller Engine (including clkgen, [rw]-path)
//----------------------------------------------------------------------------
reg fml_rd;
reg fml_wr;
wire fml_done;
wire [`FML_ADR_RNG] fml_adr;
wire [`FML_DAT_RNG] fml_wdat;
wire [`FML_BE_RNG] fml_wbe;
reg fml_wnext;
reg fml_wnext2;
wire fml_rempty;
reg fml_rnext;
wire [`FML_DAT_RNG] fml_rdat;
ddr_ctrl #(
.phase_shift( phase_shift ),
.clk_multiply( clk_multiply ),
.clk_divide( clk_divide ),
.wait200_init( wait200_init )
) ctrl0 (
.clk( clk ),
.reset( reset ),
// DDR Ports
.ddr_clk( ddr_clk ),
.ddr_clk_n( ddr_clk_n ),
.ddr_clk_fb( ddr_clk_fb ),
.ddr_ras_n( ddr_ras_n ),
.ddr_cas_n( ddr_cas_n ),
.ddr_we_n( ddr_we_n ),
.ddr_cke( ddr_cke ),
.ddr_cs_n( ddr_cs_n ),
.ddr_a( ddr_a ),
.ddr_ba( ddr_ba ),
.ddr_dq( ddr_dq ),
.ddr_dqs( ddr_dqs ),
.ddr_dm( ddr_dm ),
// FML (FastMemoryLink)
.fml_rd( fml_rd ),
.fml_wr( fml_wr ),
.fml_done( fml_done ),
.fml_adr( fml_adr ),
.fml_wdat( fml_wdat ),
.fml_wbe( fml_wbe ),
.fml_wnext( fml_wnext2 ),
.fml_rempty( fml_rempty ),
.fml_rdat( fml_rdat ),
.fml_rnext( fml_rnext ),
// DCM phase shift control
.ps_ready( ps_ready ),
.ps_up( ps_up ),
.ps_down( ps_down )
);
assign fml_adr = { ls_adr_tag, ls_adr_set };
assign fml_wdat = (ls_way) ? ls_way1_load[`WAY_DAT_RNG] :
ls_way0_load[`WAY_DAT_RNG];
assign fml_wbe = (ls_way) ? ls_way1_load[`WAY_VALID_RNG] :
ls_way0_load[`WAY_VALID_RNG];
assign ls_tag_adr = { ls_adr_set };
assign ls_wayX_adr = { ls_adr_set, ls_adr_word };
assign ls_way0_we = ls_wayX_we & ~ls_way;
assign ls_way1_we = ls_wayX_we & ls_way;
assign ls_wayX_store[`WAY_DAT_RNG] = fml_rdat;
assign ls_wayX_store[`WAY_VALID_RNG] = 4'b1111;
//----------------------------------------------------------------------------
// LS (Load and Store) Engine
//----------------------------------------------------------------------------
parameter l_idle = 0;
parameter l_fill = 1;
parameter l_spill = 2;
parameter l_waitdone = 3;
reg [2:0] ls_state;
assign ls_busy = (ls_state != l_idle) || ls_fill || ls_spill;
// Syncronous part FSM
always @(posedge clk)
begin
if (reset) begin
ls_state <= l_idle;
ls_adr_word <= 0;
fml_wr <= 0;
fml_rd <= 0;
end else begin
fml_wnext2 <= fml_wnext;
case (ls_state)
l_idle: begin
ls_adr_word <= 0;
if (ls_spill) begin
ls_state <= l_spill;
ls_adr_word <= ls_adr_word + 1;
end
if (ls_fill) begin
ls_state <= l_fill;
fml_rd <= 1;
end
end
l_spill: begin
ls_adr_word <= ls_adr_word + 1;
if (ls_adr_word == 3) begin
ls_state <= l_waitdone;
fml_wr <= 1;
end
end
l_waitdone: begin
ls_adr_word <= 0;
if (fml_done) begin
ls_state <= l_idle;
fml_wr <= 0;
end
end
l_fill: begin
if (fml_done)
fml_rd <= 0;
if (~fml_rempty)
ls_adr_word <= ls_adr_word + 1;
if (~fml_rempty & (ls_adr_word == 3))
ls_state <= l_idle;
end
endcase
end
end
always @(*)
begin
fml_wnext <= 0;
fml_rnext <= 0;
ls_tag_we <= 0;
ls_tag_store <= ls_tag_load;
ls_wayX_we <= 0;
case (ls_state)
l_idle: begin
if (ls_spill) begin
fml_wnext <= 1;
end
end
l_spill: begin
fml_wnext <= 1;
end
l_waitdone: begin
if (ls_way == 0)
ls_tag_store[`TAG_LINE_DIRTY0_RNG] <= 0;
else
ls_tag_store[`TAG_LINE_DIRTY1_RNG] <= 0;
if (fml_done)
ls_tag_we <= 1;
end
l_fill: begin
if (ls_way == 0) begin
ls_tag_store[`TAG_LINE_DIRTY0_RNG] <= 0;
ls_tag_store[`TAG_LINE_TAG0_RNG] <= ls_adr_tag;
end else begin
ls_tag_store[`TAG_LINE_DIRTY1_RNG] <= 0;
ls_tag_store[`TAG_LINE_TAG1_RNG] <= ls_adr_tag;
end
if (~fml_rempty) begin
ls_wayX_we <= 1;
fml_rnext <= 1;
end
if (~fml_rempty & (ls_adr_word == 3))
ls_tag_we <= 1;
end
endcase
end
always @(posedge clk)
begin
if (ls_fill)
$display ("At time %t WB_DDR fill cacheline: TAG = %h, SET = %h)", $time, ls_adr_tag, ls_adr_set);
if (ls_spill)
$display ("At time %t WB_DDR spill cacheline: TAG = %h, SET = %h)", $time, ls_adr_tag, ls_adr_set);
end
endmodule