【笔记】循环操作来同步数据
参考<<Verilog那些事儿-整合篇>>
我们知道模块之间的沟通需要一个时钟,若要达到两个模块之间时序的同步,虽然可以用寄存器来延时输出达到时序同步的效果,如果两个模块之间的时钟相差一个时钟的话,那么我们可以定义一个寄存器来达到同步。但是想想,若是两个模块之间的时钟相差5个或者更多的话,仅仅用寄存器来同步的话,岂不累死人。所以我们需要定义一个新的同步时序的办法,这个就是下面介绍的循环操作来同步数据。
首先给出的是用寄存器来使模块之间同步的整体的RTL图:
其次是用循环操作来同步数据:
Verilog源代码1:
module exp7_env ( input CLK, input RSTn, input Start_Sig, output Done_Sig, output [7:0]ROM_Data, output [3:0]RAM_Addr, output Write_En_Sig, /***********************/ output [3:0]SQ_ROM_Addr /**********************/ ); /*****************************/ wire [3:0]ROM_Addr; control_module U1 ( .CLK( CLK ), .RSTn( RSTn ), .Start_Sig( Start_Sig ), .Done_Sig( Done_Sig ), .ROM_Addr( ROM_Addr ), .Write_En_Sig( Write_En_Sig ), .RAM_Addr( RAM_Addr ) ); /********************************/ rom_module U2 ( .CLK( CLK ), .RSTn( RSTn ), .ROM_Addr( ROM_Addr ), .ROM_Data( ROM_Data ) ); /********************************/ assign SQ_ROM_Addr = ROM_Addr; /********************************/ endmodule
Verilog源代码2:
module rom_module ( input CLK, input RSTn, input [3:0]ROM_Addr, output [7:0]ROM_Data ); /***************************/ reg [7:0]rData; always @ ( posedge CLK or negedge RSTn ) if( !RSTn ) begin rData <= 8'd0; end else case( ROM_Addr ) 0: rData <= 8'b0111_1110; 1: rData <= 8'b0100_0010; 2: rData <= 8'b0100_0010; 3: rData <= 8'b0100_0010; 4: rData <= 8'b0100_0010; 5: rData <= 8'b0100_0010; 6: rData <= 8'b0100_0010; 7: rData <= 8'b0100_0010; 8: rData <= 8'b0100_0010; 9: rData <= 8'b0100_0010; 10: rData <= 8'b0100_0010; 11: rData <= 8'b0100_0010; 12: rData <= 8'b0100_0010; 13: rData <= 8'b0100_0010; 14: rData <= 8'b0100_0010; 15: rData <= 8'b0111_1110; endcase /***************************/ assign ROM_Data = rData; /***************************/ endmodule
Verilog源代码3:
module control_module ( input CLK, input RSTn, input Start_Sig, output Done_Sig, output [3:0]ROM_Addr, output Write_En_Sig, output [3:0]RAM_Addr ); /*****************************/ reg [1:0]i; reg [4:0]x; reg [4:0]y; reg [3:0]rROM, rRAM; reg isWrite; reg [4:0]C1; reg isDone; always @ ( posedge CLK or negedge RSTn ) if( !RSTn ) begin i <= 2'd0; x <= 5'd0; y <= 5'd0; rROM <= 4'd0; rRAM <= 4'd0; isWrite <= 1'b0; C1 <= 5'd0; isDone <= 1'b0; end else if( Start_Sig ) case( i ) //important //步骤0需要17个时钟,这17个时钟时16个循环操作+ROM模块的沟通延迟一个时钟 //if( x +1 == C1 )表示延时一个时钟 0: begin if( y == C1 ) begin y <= y + 1'b1; rROM <= y[3:0] ; end if( x +1 == C1 ) begin x <= x + 1'b1; isWrite <= 1'b1; rRAM <= x[3:0]; end if( C1 == 17 -1 ) begin C1 <= 5'd0; x <= 5'd0; y <= 5'd0; i <= i + 1'b1; end else C1 <= C1 + 1'b1; end 1: begin isWrite <= 1'b0; i <= i + 1'b1; end 2: begin isDone <= 1'b1; i <= i + 1'b1; end 3: begin isDone <= 1'b0; i <= 2'd0; end endcase /*****************************/ assign ROM_Addr = rROM; assign RAM_Addr = rRAM; assign Write_En_Sig = isWrite; assign Done_Sig = isDone; /******************************/ endmodule
仿真源代码:
`timescale 1 ps/ 1 ps module exp7_env_simulation(); reg CLK; reg RSTn; reg Start_Sig; wire Done_Sig; wire [3:0]SQ_ROM_Addr; wire [7:0]ROM_Data; wire Write_En_Sig; wire [3:0]RAM_Addr; /*******************************/ exp7_env U1 ( .CLK(CLK), .RSTn(RSTn), .Start_Sig(Start_Sig), .Done_Sig(Done_Sig), .RAM_Addr(RAM_Addr), .ROM_Data(ROM_Data), .Write_En_Sig(Write_En_Sig), .SQ_ROM_Addr(SQ_ROM_Addr) ); /*******************************/ initial begin RSTn = 0; #1000 RSTn = 1; CLK = 0; forever #25 CLK = ~CLK; end /*******************************/ reg [3:0]i; always @ ( posedge CLK or negedge RSTn ) if( !RSTn ) begin i <= 4'd0; Start_Sig <= 1'b0; end else case( i ) 0: if( Done_Sig ) begin Start_Sig <= 1'b0; i <= i + 1'b1; end else Start_Sig <= 1'b1; 1: i <= i; endcase /*******************************/ endmodule
仿真图
这里主要看步骤0的最后的一个时钟
光标1是指向步骤0的T0,在T0的时候if(y==C1) 成立然后,y递增,rROM被赋予y
的过去值,亦即0。在T0的未来y的未来值为1,rROM的未来值为0。y的循环操作执行
直到光标3指向的T15。
在光标4指向的T16,rROM理应被赋予y的过去值,亦即16,但是为什么在T16的未来,
rROM的过去值会是0而不是16?答案很简单rROM被赋予y[3:0],16的二进制是5'b10000,
所以是4'b000。同样在T16的瞬间,由于if(C1==17-1) 成立y<=5'd0;的优先级大于y<=
y+1'b1;所以在T16的未来y的未来值是0而不是17。
光标2是指向步骤0的T1,在T1的时候if(x+1==C1) 成立,rRAM被赋予x的过去值,
同时间isWrite 也被拉高。所以在T1的未来x的未来值为1,rRAM的未来值为0。x的循
环操作执行直到光标4指向的T16。很凑巧的是在T16的时候if(C1==17-1) 也成立,与其
y<=y+1'b1被执行但是优先级的关系,所以y<=5'd0被优先执行。
不过有一点不一样的是if(C1==17-1)的成立不包括拉低isWrite,isWrite更像是循环保留
的操作。isWrite 在步骤0的T1被拉高(光标2),由于它需要和rRAM同步,所以isWrite
也必须拉高16个时钟的时间,亦即T1~16(光标2~4)。直到步骤1(光标5)的时候它才被
拉低。