1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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
|