非阻塞: <=
阻塞: =
题目
异步时钟域数据复用设计
1、相关的输入输出信号及时序关系:
图中的TS_IN[7:0]、CLK、SYNC分别为TS传输流的数据信号、字节时钟、同步信号。时序图如下:
TS传输流的基本单元是帧结构,分为数据帧和空帧。假设每帧包含10个字节,第一个字节为同步字节,即H“FF”;空帧的标志是第二字节和第三字节全为H“EE”。
DIN[7:0]、CLK_W、EN分别是需要复用的控制数据、相应的字节时钟和数据使能。时序图如下:
RESET是系统的复位信号。
TS_OUT[7:0]是复用了DIN数据的TS传输流,码率与输入TS传输流相同。
注意:时钟CLK与CLK_W是异步的。本设计假定为CLK为10MHz,CLK_W为3MHz。
2、设计要求:假设TS传输流中的空帧足够多,要求将某些空帧的数据区(共7个数据)全部换为数据DIN(帧同步字节和空帧标志不变),按照TS传输流格式进行传输。TS传输流数据帧中的数据和DIN数据不能出现丢失。
程序
顶层
module top_data_mux
(
input rst_n,
input clk_10m,
input [7:0] ts_in,
input sync,
input clk_3m,
input [7:0] din,
input en,
output [7:0] ts_out
);
wire [7:0] q;
wire rdreq;
fifo u0_fifo
(
.q (q),
.data (din),
.wrreq (en),
.wrclk (clk_3m),
.rdreq (rdreq),
.rdclk (clk_10m)
);
ts_control u0_ts_control
(
.ts_out (ts_out),
.rdreq (rdreq),
.q (q),
.ts_in (ts_in),
.clk_10m (clk_10m),
.rst_n (rst_n),
.sync (sync)
);
endmodule
ts_control模块
module ts_control
(
input clk_10m,
input rst_n,
input sync,
input [7:0] ts_in,
input [7:0] q,
output reg rdreq,
output [7:0] ts_out
// output reg [7:0] ts_out
);
//reg [3:0] cur_state ;
reg [3:0] next_state ;
reg [3:0] cnt ;
reg swich_en;
reg cnt_en;
localparam s1 = 4'b0001;
localparam s2 = 4'b0010;
localparam s3 = 4'b0100;
localparam s4 = 4'b1000;
//assign rdreq = (next_state == s4)? 1'b1:1'b0 ;
assign ts_out = (swich_en == 1'b1)? q : ts_in ;
//(三段式状态机)同步时序描述状态转移
//always @(posedge clk_10m or negedge rst_n) begin
// if(rst_n == 1'b0)
// cur_state <= s1;
// else
// cur_state <= next_state;
//end
always @(posedge clk_10m or negedge rst_n) begin
if(rst_n == 1'b0) begin
next_state<= s1 ;
cnt_en <= 1'b0;
rdreq <= 1'b0;
swich_en <= 1'b0;
end
else begin
case(next_state)
s1: begin
if(ts_in==8'hff && sync==1'b1)
next_state <= s2;
else
next_state <= s1;
end
s2: begin
if(ts_in == 8'hee)begin
next_state <= s3;
// rdreq <= 1'b1;
end
else
next_state <= s1;
end
s3: begin
if(ts_in == 8'hee)begin
next_state <= s4;
rdreq <= 1'b1;
cnt_en <= 1'b1;
// swich_en <= 1'b1;
end
else
next_state <= s1;
end
s4: begin
// swich_en <= 1'b1;
if(cnt == 4'd6)begin
rdreq <= 1'b0;
cnt_en <= 1'b0;
next_state <= s1;
end
// else if(cnt == 4'd7)begin
// next_state <= s1;
// swich_en <= 1'b0;
// end
else
next_state <= s4;
end
default : ;
endcase
end
end
//always @(posedge clk_10m or negedge rst_n)
//begin
// if(!rst_n)
// ts_out <= 8'h00;
// else if(swich_en)
// ts_out <= q ;
// else
// ts_out <= ts_in ;
//end
always @(posedge clk_10m or negedge rst_n)
begin
if(!rst_n)
swich_en <= 1'd0;
else if(cnt <= 4'd6 && next_state == s4)
swich_en <= 1'd1;
else
swich_en <= 1'd0;
end
always @(posedge clk_10m or negedge rst_n)
begin
if(!rst_n)
cnt <= 4'd0;
else if(cnt_en == 1'b1)
cnt <= cnt + 4'b1;
else
cnt<=4'd0;
end
endmodule
仿真
分析
<=赋值是把时钟上升沿前时刻的数据值作为,时钟上升沿时刻的数据值
ts_in在输入0xff之后,next_state的值立马变成0010,但是在0xff的这个周期内,其他采用<=赋值方法采样到的还是0001。之前我一直理解的是<=赋值会在下一个clk才生效,即在0xff的这个时间段next_state还是0001,这样理解会使对周期时序要求严格的程序产生错误。
在s3状态时,检测到了0xee后,rdreq和cnt_en被立马置1,但是其他采用<=赋值方法的模块是采不到rdreq和cnt_en为1,要到下一个clk才能采到。所以对于fifo模块,rdreq被置1的第一个clk时期,fifo其实是没有读到rdreq为1,第二个clk时,fifo收到了rdreq,立马输出了第一个数据,可以把它理解成<=赋值,虽然立马输出了第一个数据,但是其他模块读到的还是输出之前的数据,不信的话可以自己仿真试试。对于这个题目而言,要马上采到fifo的输出数据,补到ts_out上,不然结果有问题。所以为了其他模块能够采到fifo立马输出的这个数据,采用=赋值。保证swich_en == 1’b1的时刻,正好输出第一个q就行。
assign ts_out = (swich_en == 1’b1)? q : ts_in ;
swich_en 置1条件cnt <= 4’d6 && next_state == s4 。 虽然在检测到第二个0xee时,next_state就是s4了,但是由于采用<=赋值;且这个判断条件
cnt <= 4’d6 && next_state == s4
处于一个<=赋值的模块。但凡这两个条件中有一个是=赋值,都能在next_state 等于 s4 的当个周期让swich_en 置1,所以程序中swich_en的置1,在next_state 等于 s4 的第二个clk。
同样的,可以自己理解一下cnt和cnt_en之间的关系。