题目描述
一道全志科技数字前端的面试题,详见下图。
观察题目波形,马上意识到这是一个跨时钟域的串并转换。既然知道了电路是干什么的,编码就非常简单了。
代码
注意:wrb 是 b 时钟域的一个信号,因此在 a 时钟域接收数据完毕后产生的 flag 应进行展宽,将其高电平至少维持两个 Ta,以保证 b 时钟域一定能检测到。
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// McEv0y
// Create Date: 2020/09/12 17:17:37
// Design Name:
// Module Name: tb_trans
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
// A tranport circuit which has a serial input in clka and a parallel output in clkb.
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module memi
#(
parameter DATA_WIDTH = 8
)
(
//---------------<输入信号>------------------
input clka,
input clkb,
input da,//data in clka
input wra_n,//write enable signal which is effective while negative
input rst_n,//asynchronize reset
//---------------<输出信号>-------------------
output reg wr_b,
output reg [DATA_WIDTH-1 : 0] db
);
reg finished_a, finished_a_r;
reg [DATA_WIDTH-1 : 0] cnt;
reg [DATA_WIDTH-1 : 0] buffer;
always@ (posedge clka or negedge rst_n)
begin
if(!rst_n) begin
finished_a <= 0;
finished_a_r <= 0;
cnt <= DATA_WIDTH-1;
end
else begin
if(!wra_n) begin
buffer[cnt] <= da;
finished_a_r <= finished_a;
if(cnt == 0) begin
cnt <= DATA_WIDTH - 1;
finished_a <= 1;
end
else begin
cnt <= cnt - 1;
finished_a <= 0;
end
end//if(!wra_n)
end//else
end//always
always@ (posedge clkb or negedge rst_n)
begin
if(!rst_n) begin
wr_b <= 0;
db <= 'h0;
end
else begin
if(finished_a || finished_a_r) begin
wr_b <= 1;
db <= buffer;
end//if
else
wr_b <= 0;
end//else
end//always
endmodule
testbench
testbench 代码如下:
`timescale 1ns / 1ps
module tb_trans;
reg clka, clkb, wra_n, da, rst_n;
wire wrb;
wire [7 : 0] db;
memi m1
(
.clka(clka),
.clkb(clkb),
.da(da),
.wra_n(wra_n),
.rst_n(rst_n),
.wr_b(wrb),
.db(db)
);
initial begin
rst_n = 0;
clka = 0;
clkb = 0;
@(negedge clkb)
rst_n = 1;
@(negedge clka)
wra_n = 0;
da = 1;
repeat(7) begin
@(negedge clka)
da = $random;
end
@(posedge wrb)
wra_n = 1;
end
parameter PERIOD = 4;
always begin
#(PERIOD / 4) clka = ~clka;
end
always begin
#(PERIOD / 2) clkb = ~clkb;
end
endmodule
下图的仿真结果证明逻辑正确。