I2S 发送器 Verilog 实现不工作
I2S Transmitter Verilog Implementation not working
我正在尝试在 verilog 中实现 I2S 发送器。它的数据表位于:https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf
我写了代码,但是当我测试它时,我的SD线延迟了1个时钟周期。
有人可以检查我的实现吗?
module Transmiter(
input signed [23:0] DLeft, input signed [23:0] DRight, input WS, input CLK,
output reg SD
);
wire PL;
reg Q1,Q2;
reg [23:0] shift_reg;
reg [23:0] Tdata;
assign PL = Q1^Q2;
always @(posedge CLK)
begin
Q1 <= WS;
Q2 <= Q1;
end
always @( Q1) begin
if (Q1)
begin
Tdata <= DRight;
end
else
begin
Tdata <= DLeft;
end
end
always @(negedge CLK)
begin
if(PL)
begin
shift_reg <= Tdata;
end
else begin
SD <= shift_reg[23];
shift_reg <= {shift_reg[22:0],1'b0};
end
end
endmodule
编辑:这是波形图像image
测试台代码:
module Transmitter_tb(
);
reg CLK, WS;
reg [23:0] dataL;
reg [23:0] dataR;
wire SDout;
Transmiter UT(dataL, dataR, WS, CLK, SDout);
initial begin
dataL = 24'hF0F0FF; #2;
dataR = 24'h0000F0; #2;
end
always begin
CLK=0; #20;
CLK=1; #20;
end;
always begin
WS=0; #1000;
WS=1; #1000;
end;
endmodule
您的 negedge
块包含一个 if-else
构造,并且只会在单个时钟边沿上计算一个或另一个。因此,当 PL
为高时,SD
不会更改值。
此外,您在代码中使用了非阻塞赋值 (<=
)。这大致意味着直到 always 块结束时才会评估更改。因此,即使 SD <= shift_reg[23]
在 shift_reg <= Tdata
之后,它也不会采用 shift_reg[23]
中的新值,而是使用以前的值。如果您希望 SD
在 shift_reg[23]
更改时立即更改,您需要组合执行此操作。
这应该有效:
always @(negedge CLK)
begin
if(PL)
begin
shift_reg <= Tdata;
end
else
shift_reg <= {shift_reg[22:0],1'b0};
end
assign SD = shift_reg[23];
工作示例:https://www.edaplayground.com/x/4bPv
附带说明一下,我仍然不相信 DRight
和 DLeft
实际上是常量,我可以看到它们在您的 TB 中,但数据没有意义因为你的 I2S 是恒定的。您当前的构造可能会生成一个锁存器(而不是 MUX),而我们通常不希望在我们的设计中使用它们。
您应该清理阻塞语句与非阻塞语句的使用:
始终在时钟语句中使用非阻塞赋值,即“<=”。
始终在组合(非时钟)语句中使用阻塞赋值,即“=”。
这是全行业的建议,并非个人意见。您可以在很多地方找到此推荐,例如:
http://web.mit.edu/6.111/www/f2007/handouts/L06.pdf
不完整的敏感列表(正如@Hida 所指出的)也可能导致问题。
尝试纠正这两个问题,看看它是否开始按预期工作。
另请注意,您正在使用 Q1(以及其他信号)生成 PL 信号。如果 WS 输入与您的本地时钟不同步(我假设不是),您需要在开始使用输出之前再放置一个触发器(即两个串联),以避免亚稳态问题。但是您不会在 RTL 仿真中看到这一点。
我正在尝试在 verilog 中实现 I2S 发送器。它的数据表位于:https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf
我写了代码,但是当我测试它时,我的SD线延迟了1个时钟周期。 有人可以检查我的实现吗?
module Transmiter(
input signed [23:0] DLeft, input signed [23:0] DRight, input WS, input CLK,
output reg SD
);
wire PL;
reg Q1,Q2;
reg [23:0] shift_reg;
reg [23:0] Tdata;
assign PL = Q1^Q2;
always @(posedge CLK)
begin
Q1 <= WS;
Q2 <= Q1;
end
always @( Q1) begin
if (Q1)
begin
Tdata <= DRight;
end
else
begin
Tdata <= DLeft;
end
end
always @(negedge CLK)
begin
if(PL)
begin
shift_reg <= Tdata;
end
else begin
SD <= shift_reg[23];
shift_reg <= {shift_reg[22:0],1'b0};
end
end
endmodule
编辑:这是波形图像image
测试台代码:
module Transmitter_tb(
);
reg CLK, WS;
reg [23:0] dataL;
reg [23:0] dataR;
wire SDout;
Transmiter UT(dataL, dataR, WS, CLK, SDout);
initial begin
dataL = 24'hF0F0FF; #2;
dataR = 24'h0000F0; #2;
end
always begin
CLK=0; #20;
CLK=1; #20;
end;
always begin
WS=0; #1000;
WS=1; #1000;
end;
endmodule
您的 negedge
块包含一个 if-else
构造,并且只会在单个时钟边沿上计算一个或另一个。因此,当 PL
为高时,SD
不会更改值。
此外,您在代码中使用了非阻塞赋值 (<=
)。这大致意味着直到 always 块结束时才会评估更改。因此,即使 SD <= shift_reg[23]
在 shift_reg <= Tdata
之后,它也不会采用 shift_reg[23]
中的新值,而是使用以前的值。如果您希望 SD
在 shift_reg[23]
更改时立即更改,您需要组合执行此操作。
这应该有效:
always @(negedge CLK)
begin
if(PL)
begin
shift_reg <= Tdata;
end
else
shift_reg <= {shift_reg[22:0],1'b0};
end
assign SD = shift_reg[23];
工作示例:https://www.edaplayground.com/x/4bPv
附带说明一下,我仍然不相信 DRight
和 DLeft
实际上是常量,我可以看到它们在您的 TB 中,但数据没有意义因为你的 I2S 是恒定的。您当前的构造可能会生成一个锁存器(而不是 MUX),而我们通常不希望在我们的设计中使用它们。
您应该清理阻塞语句与非阻塞语句的使用:
始终在时钟语句中使用非阻塞赋值,即“<=”。
始终在组合(非时钟)语句中使用阻塞赋值,即“=”。
这是全行业的建议,并非个人意见。您可以在很多地方找到此推荐,例如:
http://web.mit.edu/6.111/www/f2007/handouts/L06.pdf
不完整的敏感列表(正如@Hida 所指出的)也可能导致问题。
尝试纠正这两个问题,看看它是否开始按预期工作。
另请注意,您正在使用 Q1(以及其他信号)生成 PL 信号。如果 WS 输入与您的本地时钟不同步(我假设不是),您需要在开始使用输出之前再放置一个触发器(即两个串联),以避免亚稳态问题。但是您不会在 RTL 仿真中看到这一点。