diff options
Diffstat (limited to 'projects/U50-HBM/verilog/cmd_gen.v')
| -rw-r--r-- | projects/U50-HBM/verilog/cmd_gen.v | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/projects/U50-HBM/verilog/cmd_gen.v b/projects/U50-HBM/verilog/cmd_gen.v new file mode 100644 index 0000000..3893073 --- /dev/null +++ b/projects/U50-HBM/verilog/cmd_gen.v @@ -0,0 +1,189 @@ +`include "project.vh" +`include "parameters.vh" + +// The DRAM Bender programmmable core that generates 4 DDR instructions per cycle and is clocked at 1/4 DDR4 memory clock +// This is due to the fact that the DDR4 PHY interface can process 4 commands per dfi_clock cycle + +// Unlike the DDR4 PHY interface, the HBM PHY can process 2 commands per dfi_clock cycle +// Since DRAM Bender is designed in a way to generate 4 DDR instructions, we will buffer 4 instructions per fab_clock cycle, +// and clock the HBM_adapter at 2x fab_clock (e.g. dfi_clock = 2x fab_clock). And then we will have HBM_clock = 2x dfi_clock. +// In this way, we will still be able to process 4 instructions per fab_clock cycle. + +module cmd_gen #(parameter CKE_WIDTH = 1, RANK_WIDTH = 1, DQ_WIDTH = 64, DRAM_CMD_SLOTS = 4, + DATA_BUF_ADDR_WIDTH = 5, DBUF_WIDTH = 4, DQ_BURST = 8) + ( + // common signals + input clk, + input rst, + //input dfi_clk, + //input dfi_rst_n, + + //other control signals + //input init_calib_complete, + //input process_iseq, + + // ddr_pipeline <-> outer module if + input [3:0] ddr_write, + input [3:0] ddr_read, + input [3:0] ddr_pre, + input [3:0] ddr_act, + input [3:0] ddr_ref, + input [3:0] hbm_sel_ch, // used for channel select + input [3:0] ddr_nop, + input [3:0] ddr_ap, + //input [3:0] ddr_half_bl, + input [3:0] ddr_rank, // can be used to indicate PC + input [3:0] ddr_pall, + input [4*`HBM_CH_WIDTH-1:0] hbm_ch, + input [4*`BG_WIDTH-1:0] ddr_bg, + input [4*`BANK_WIDTH-1:0] ddr_bank, + input [4*`COL_WIDTH-1:0] ddr_col, + input [4*`ROW_WIDTH-1:0] ddr_row, + input [511:0] ddr_wdata, + + //output [4*`ROW_ADDR_WIDTH-1:0] row_addr_4, + //output [4*`COL_ADDR_WIDTH-1:0] col_addr_4, + //output [4*`BA_ADDR_WIDTH-1:0] ba_addr_4, + //output [511:0] wrdata_4, + //output [4*`CMD_TYPE_WIDTH-1:0] cmd_type_4, + //output [4*`PC_WIDTH-1:0] BA4_4, // indicates target PC + + output [127:0] fifo_data, + output [1023:0] wrdata // have one output of 1024 bit for fifo, the other signals are now wires. Make sure to layout data as needed. + + ); + + reg [4*`ROW_ADDR_WIDTH-1:0] row_addr_ns; + reg [4*`COL_ADDR_WIDTH-1:0] col_addr_ns; + reg [4*`BA_ADDR_WIDTH-1:0] ba_addr_ns; + reg [511:0] wrdata_ns; + reg [4*`CMD_TYPE_WIDTH-1:0] cmd_type_ns; + reg [4*`PC_WIDTH-1:0] BA4_ns; + reg [4*`HBM_CH_WIDTH-1:0] channel_id_ns; + + reg [4*`ROW_ADDR_WIDTH-1:0] row_addr_r; + reg [4*`COL_ADDR_WIDTH-1:0] col_addr_r; + reg [4*`BA_ADDR_WIDTH-1:0] ba_addr_r; + reg [511:0] wrdata_r; + reg [4*`CMD_TYPE_WIDTH-1:0] cmd_type_r; + reg [4*`PC_WIDTH-1:0] BA4_r; + reg [4*`HBM_CH_WIDTH-1:0] channel_id_r; + + integer i; + + /* + assign row_addr_4 = row_addr_r; + assign col_addr_4 = col_addr_r; + assign ba_addr_4 = ba_addr_r; + assign wrdata_4 = wrdata_r; + assign cmd_type_4 = cmd_type_r; + assign BA4_4 = BA4_r; + */ + + assign fifo_data[127:64] = { channel_id_r[0 +: 2*`HBM_CH_WIDTH], + BA4_r[0 +: 2*`PC_WIDTH], + ba_addr_r[0 +: 2*`BA_ADDR_WIDTH], + col_addr_r[0 +: 2*`COL_ADDR_WIDTH], + row_addr_r[0 +: 2*`ROW_ADDR_WIDTH], + cmd_type_r[0 +: 2*`CMD_TYPE_WIDTH] }; + + assign fifo_data[63:0] = { channel_id_r[2*`HBM_CH_WIDTH +: 2*`HBM_CH_WIDTH], + BA4_r[2*`PC_WIDTH +: 2*`PC_WIDTH], + ba_addr_r[2*`BA_ADDR_WIDTH +: 2*`BA_ADDR_WIDTH], + col_addr_r[2*`COL_ADDR_WIDTH +: 2*`COL_ADDR_WIDTH], + row_addr_r[2*`ROW_ADDR_WIDTH +: 2*`ROW_ADDR_WIDTH], + cmd_type_r[2*`CMD_TYPE_WIDTH +: 2*`CMD_TYPE_WIDTH] }; + + assign wrdata[511:0] = wrdata_r; // here we are writing double the data redundantly. If we can fix it its better. + assign wrdata[1023:512] = wrdata_r; + + + always @ (*) + begin + row_addr_ns = {4*`ROW_ADDR_WIDTH{1'b0}}; + col_addr_ns = {4*`COL_ADDR_WIDTH{1'b0}}; + ba_addr_ns = {4*`BA_ADDR_WIDTH{1'b0}}; + cmd_type_ns = {4*`CMD_TYPE_WIDTH{1'b1}}; + BA4_ns = {4*`PC_WIDTH{1'b0}}; + wrdata_ns = {512{1'b0}}; + channel_id_ns = {4*`HBM_CH_WIDTH{1'b0}}; + + for(i = 0 ; i < 4 ; i = i + 1) begin + if (ddr_write[i]) begin + if (ddr_ap[i]) + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `WRA; + else + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `WR; + end + else if (ddr_read[i]) begin + if (ddr_ap[i]) + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RDA; + else + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RD; + end + else if (ddr_pre[i]) begin + if (ddr_pall[i]) + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `PREA; + else + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `PREE; + end + else if (ddr_act[i]) begin + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `ACTT; + end + else if (ddr_ref[i]) begin + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `REFF; + end + else if (ddr_nop[i]) begin + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP; + end + else if (hbm_sel_ch[i]) begin // this is now a command to select channel + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP; + // duplicate channel ID four times. This would make it easier to appropriately select channel. + // This is fine since we initially assume we would need to wait for some delay before switching between channels + channel_id_ns[0 +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH]; + channel_id_ns[`HBM_CH_WIDTH +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH]; + channel_id_ns[`HBM_CH_WIDTH*2 +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH]; + channel_id_ns[`HBM_CH_WIDTH*3 +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH]; + end + else begin + cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP; + end + + row_addr_ns[`ROW_ADDR_WIDTH*i +: `ROW_ADDR_WIDTH] = ddr_row[`ROW_WIDTH*i +: `ROW_ADDR_WIDTH]; + col_addr_ns[`COL_ADDR_WIDTH*i +: `COL_ADDR_WIDTH] = ddr_col[`COL_WIDTH*i +: `COL_ADDR_WIDTH]; + ba_addr_ns[`BA_ADDR_WIDTH*i + `BANK_WIDTH +: `BG_WIDTH] = ddr_bg[`BG_WIDTH*i +: `BG_WIDTH]; + ba_addr_ns[`BA_ADDR_WIDTH*i +: `BANK_WIDTH] = ddr_bank[`BANK_WIDTH*i +: `BANK_WIDTH]; + BA4_ns[i] = ddr_rank[i]; // In our case it represents the PC + end + + wrdata_ns = ddr_wdata; + + end + + always @(posedge clk) begin // slow clock + if(rst) begin + row_addr_r <= {4*`ROW_ADDR_WIDTH{1'b0}}; + col_addr_r <= {4*`COL_ADDR_WIDTH{1'b0}}; + ba_addr_r <= {4*`BA_ADDR_WIDTH{1'b0}}; + BA4_r <= {4*`PC_WIDTH{1'b0}}; + wrdata_r <= {512{1'b0}}; + cmd_type_r <= {4*`CMD_TYPE_WIDTH{1'b1}}; + channel_id_r <= {4*`HBM_CH_WIDTH{1'b0}}; + end + else begin + row_addr_r <= row_addr_ns; + col_addr_r <= col_addr_ns; + ba_addr_r <= ba_addr_ns; + BA4_r <= BA4_ns; + wrdata_r <= wrdata_ns; + cmd_type_r <= cmd_type_ns; + + if (hbm_sel_ch[3] | hbm_sel_ch[2] | hbm_sel_ch[1] | hbm_sel_ch[0]) begin // only update channel_id when we have a select channel command. It always needs to be the first command out of the 4. + channel_id_r <= channel_id_ns; + end else begin // otherwise, keep value from last select channel command + channel_id_r <= channel_id_r; + end + end + end + +endmodule |
