以按行写按列读的交织器为例:
//按C语言写法
always@(posedge clk or negedge rstn)
begin
if(!rstn)
begin
rdata<=0;
end else if (!w_r)begin//写入
for (Line=0;Line<=ROM_DEPTH;Line=Line+1)
for(Column=0;Column<=ROM_WIDTH;Column=Column+1)
memory[Line][Column]<=wdata;
end else if (w_r) begin//读出
for(Column=0;Column<=ROM_WIDTH;Column=Column+1)
for (Line=0;Line<=ROM_DEPTH;Line=Line+1)
rdata<=memory[Line][Column];
end else begin
rdata<=1'bx;
end
end
但以上语句是不可综合的。
拆解思路一:硬件电路要时刻考虑时序,我们用一个时钟驱动计数器累加,由计数器的不同数值来触发事件,可综合语句case语句来做触发,例如
always@(posedge clk)
begin
if(addr==2'b11)begin
addr <= 2'b00;
end else else begin
addr <= addr + 1'b1;
end
end
case(addr)
00: rdata <= memory[00][7:0];
01: rdata <= memory[01][7:0];
10: rdata <= memory[10][7:0];
11: rdata <= memory[11][7:0];
//以上代码就等效于
for(addr=0;addr<=3;addr++)
rdata=memory[addr][7:0];
拆解思路二:如果情况很多时,case语句的劣势在于必须一一列举,例如有嵌套语句时,列举所有情况太过冗长。在存储器的例子中,实际上地址还是可以作为变量
always@(posedge clk or negedge rstn)
begin
if(!rstn)begin
Column <= 3'b0;
Line <= 3'b0;
end else if(~w_r) begin//写入数据
if (Column==ROM_WIDTH-1)begin
//完成一次内层嵌套的循环
//此时写完了一行,Line++
Column <= 3'b0;
if(Line==ROM_DEPTH-1)begin
Line <= 3'b0;
end else begin
Line <= Line + 3'b001;
end
end else begin
Column <= Column + 3'b001;
end
memory[Line][Column] <= wdata;
end else if (w_r) begin
//读出数据
if (Line==ROM_DEPTH-1)begin
//完成一次内层嵌套的循环
//读完了一列,Column++
Line <= 3'b0;
if(Column==ROM_WIDTH-1)begin
Column <= 3'b0;
end else begin
Column <= Column + 3'b001;
end
end else begin
Line <= Line + 3'b001;
end
rdata <= memory[Line][Column];
end
end
写for循环注意的要点
(1)从最大的不同点进行分类:本例子特点为按行写按列读,写入时先固定一行,扫描列写入,因此外层循环变量为行,内层嵌套为列;读出时按列读出,先固定一列,扫描行读出,因此外层循环变量为列,内层为行。
若行宽=列深,则行列是对称的,可以对其进行降维,也就是说不以读写行为作为分类,将行列互换即可。
if(~w_r) begin
//write
memory[Line][Column]<=wdata;
end else if(w_r) begin
//read
rdata<=memory[Column][Line];
end
(2)注意边界:例如[7:0]Line,Line最大值为255,实际上Line==255 保持了一个时钟周期;
若约束条件为 if (Line= =256),要分情况讨论
Line位宽为[8:0]时,Line= =256会保持一个周期,但存储器实际上没有这个地址,导致写入失效,读出为高阻;
Line位宽为[7:0]时,Line++会回0,if (Line= =256)内的语句永远不会被执行。
(3)先判断后加/先加后判断:由于(2)所述的边界问题,会有不同的情形
一般情况下,由于变量的位宽限制,会先判断后自加
if (Line==ROM_DEPTH-1)begin
Line <= 3'b0;
end else begin
Line <= Line +1'b1;
end
但有些情况下,可能会先加后判断,例如在连续读写数据时,地址的跳位要求
begin
Line <= Line + 9'h20;
if(Line==256)begin
Line <= 9'h0;
end
end
上例中,连续读32位,第一次为读取地址为[31:0],下一次Line为32,64,96,128,160,196,224,256,Line=224时,整个Memory的数据已经读完了,因此要再加一次32使之回0。如果先判断后自加,则Line==256会保持一个时钟周期,导致读出为高阻。为消除这种状况,应使Line= =256后立即回0(仍在一个时钟周期之内)。