框图
-
THR:发送保持寄存器
- 定义了两种状态:空,满
- 数据写入端口地址:00H
- 状态读出端口地址:00H
- 当THR不满时,可以向THR写入数据
-
TSR:发送移位寄存器
- 一旦TSR空而THR中有数据时,THR中的数据就送到TSR
- RSR中的数据以串行方式从TxD段发送,高位在前,低位在后
- 在TxD端的bit流中,若连续出现5个’1‘,则在第5个’1‘之后自动插入一个’0’。注意:相邻两个字节之间也会出现5个连续的‘1’
-
发送时许
- 代码
module p2s_transmitter (
RST,CLK2M,CS,FS,WR,RD,A0,D,TxD
);
input RST,CLK2M,CS,WR,RD,FS;
input A0;
inout [7:0] D;
output TxD;
reg TxD;
wire [7:0] D;
reg [7:0] THR_Status,THR,TSR,R_Shifter;
// R_Shifter 最低为TxD_1;
reg [2:0] Count8;
reg [2:0] Count5;
reg [4:0] Count32;
reg TSR_Shift_EN;
reg Mux_Sel1,Mux_Sel2;
reg TxD_2;
reg Load_EN;
reg THR_Read;
// 移位寄存器,load操作为:每当移完一个字节的数据并且THR不空时,
// 就从THR加载一个数据
// 在TSR_Shift_EN信号有效时,TSR做移位操作
always @(posedge RST or posedge CLK2M) begin
if (RST)
TSR <= 8'b00000000;
else if (Count8 ==3'b000&&THR_Status[0] == 1) begin
TSR <= THR;
end else if (TSR_Shift_EN == 1'b1)
TSR <= {TSR[6:0],1'b0};
end
//连续5个‘1’的检测及插‘0’。
always @(posedge RST or posedge CLK2M) begin
if (RST)
Count5 = 3'b100;
else if (TSR[7] == 1'b0)
Count5 = 3'b100;
else
Count5 = Count5 - 1;
end
always @(TSR or Mux_Sel1) begin
if (Mux_Sel1)
TxD_2 = 1'b0;
else
TxD_2 = TSR[7];
end
always @(Count5) begin
if (Count5 == 3'b000)
Mux_Sel1 = 1'b1;
else
Mux_Sel1 = 1'b0;
end
//在发送5个连续的‘1’之后要插入一个‘0’,此时TSR不移位。
// always @(Count5) begin
// if (Count5 == 3'b000)
// TSR_Shift_EN = 1'b0;
// else
// TSR_Shift_EN = 1'b1;
// end 等价于
always @(Mux_Sel1) begin
TSR_Shift_EN = ~Mux_Sel1;
end
//正常数据与发送7EH的处理
always @(Mux_Sel1 or TxD or R_Shifter) begin
if (Mux_Sel2)
TxD = R_Shifter[7];
else
TxD = TxD_2;
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
R_Shifter = 8'b01111110;
else if (Load_EN)
R_Shifter = 8'b01111110;//7EH
else
R_Shifter = {R_Shifter[6:0],R_Shifter[7]};
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
Count32 = 5'b11111;
else if (~FS)
Count32 = 5'b11111;
else if (TSR_Shift_EN)
Count32 = Count32 - 1;
end
// 对TSR移位32次,即进入空闲态,因此Count32对TSR_Shift_EN进行计数
always @(Count32) begin
if (Count32 == 5'b00000)
Load_EN = 1'b1;
else
Load_EN = 1'b0;
end
always @(Load_EN) begin
Mux_Sel2 = Load_EN;
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
THR = 8'b00000000;
else if (CS == 1'b0&&WR == 1'b0&&A0 == 1'b0)
THR = D;
end
//THR_Status是一个状态信号,由于该状态最终可以被处理器通过总线读取,因此可以将它设置为
//8bits,当THR中有数据时为00000001,否则为00000000;
// 该信号应该是寄存器信号,无论THR中是否有数据,只要TSR发出一个读信号,那么在
// 读信号有效的下一个时钟沿,该信号为空
always @(posedge RST or posedge CLK2M) begin
if (RST)
THR_Status = 8'b00000000;
else if (WR == 1'b0 && THR_Read == 1'b1 &&A0 == 1'b0)
THR_Status = 8'b00000001;
else if (WR == 1'b1 && THR_Read == 1'b0)
THR_Status = 8'b00000000;
end
always @(Count8) begin
if (Count8 == 3'b000)
THR_Read = 1'b0;
else
THR_Read = 1'b1;
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
Count8 = 3'b000;
else if (~FS)
Count8 = 3'b111;
else if (TSR_Shift_EN)
Count8 = Count8 - 1;
end
assign D = (RD == 1'b0 &&A0 == 1'b0)?THR_Status:8'bzzzzzzzz;
endmodule
更新Design Source文件
判断是否插‘0’以及循环移位寄存器使能信号的产生;使用Count5和时钟上升沿配合完成Mux_Sel1信号的置一。因为THR_Read信号的变化预示着一个字节传输的完成,所以当该信号上升沿出现时,将Count5置为3’b100。
always @(posedge CLK2M) begin
if (Count5 == 3'b111)
Mux_Sel1 = 1'b1;
else
Mux_Sel1 = 1'b0;
end
由于Count5作为一个重要的信号,在笔者处理仿真过程中发现,如果能够改成如下代码,才能正确实现笔者所编写Design source的功能,即使用时钟上升沿和THR_Read上升沿触发,而且在Count53’b111时才暂停数据的传输,此时也将移位使能置零,然后在该时钟周期插入‘0’,所以需要当Count50,减一之后为3’b111,此时需要置为3’b100。
always @(posedge RST or posedge CLK2M or posedge THR_Read) begin
if (RST)
Count5 = 3'b000;
else if (TSR[7] == 1'b0||Count5 == 3'b111||THR_Read == 1'b0)
Count5 = 3'b100;
else
Count5 = Count5 - 1;
end
always @(TSR or Mux_Sel1) begin
if (Mux_Sel1)
TxD_2 = 1'b0;
else
TxD_2 = TSR[7];
end
always @(Mux_Sel1) begin
TSR_Shift_EN = ~Mux_Sel1;
end
完整代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/12 18:18:24
// Design Name:
// Module Name: p2s_transmitter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module p2s_transmitter (
RST,CLK2M,CS,FS,WR,RD,A0,D,TxD
);
input RST,CLK2M,CS,WR,RD,FS;
input A0;
inout [7:0] D;
output TxD;
reg TxD;
wire [7:0] D;
reg [7:0] THR_Status,THR,TSR,R_Shifter;
// R_Shifter 最低为TxD_1;
reg [2:0] Count8;
reg [2:0] Count5;
reg [4:0] Count32;
reg TSR_Shift_EN;
reg Mux_Sel1,Mux_Sel2;
reg TxD_2;
reg Load_EN;
reg THR_Read;
reg state;
// 移位寄存器,load操作为:每当移完一个字节的数据并且THR不空时,
// 就从THR加载一个数据
// 在TSR_Shift_EN信号有效时,TSR做移位操作
always @(posedge RST or posedge CLK2M) begin
if (RST)
TSR <= 8'b00000000;
else if ((Count8 == 3'b000&&THR_Status[0] == 1'b1)) begin
TSR <= THR;
end else if (TSR_Shift_EN == 1'b1)
TSR <= {TSR[6:0],1'b0};
end
//连续5个‘1’的检测及插‘0’。
always @(posedge RST or posedge CLK2M or posedge THR_Read) begin
if (RST)
Count5 = 3'b000;
else if (TSR[7] == 1'b0||Count5 == 3'b111||THR_Read == 1'b0)
Count5 = 3'b100;
else
Count5 = Count5 - 1;
end
always @(TSR or Mux_Sel1) begin
if (Mux_Sel1)
TxD_2 = 1'b0;
else
TxD_2 = TSR[7];
end
always @(posedge CLK2M) begin
if (Count5 == 3'b111)
Mux_Sel1 = 1'b1;
else
Mux_Sel1 = 1'b0;
end
//在发送5个连续的‘1’之后要插入一个‘0’,此时TSR不移位。
// always @(Count5) begin
// if (Count5 == 3'b000)
// TSR_Shift_EN = 1'b0;
// else
// TSR_Shift_EN = 1'b1;
// end 等价于
always @(Mux_Sel1) begin
TSR_Shift_EN = ~Mux_Sel1;
end
//正常数据与发送7EH的处理
always @(TxD or R_Shifter or TSR) begin
if (Mux_Sel2)
TxD = R_Shifter[7];
else
TxD = TxD_2;
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
R_Shifter = 8'b01111110;
else if (Load_EN)
R_Shifter = 8'b01111110;//7EH
else
R_Shifter = {R_Shifter[6:0],R_Shifter[7]};
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
Count32 = 5'b11111;
else if (~FS)
Count32 = 5'b11111;
else if (TSR_Shift_EN)
Count32 = Count32 - 1;
end
// 对TSR移位32次,即进入空闲态,因此Count32对TSR_Shift_EN进行计数
always @(Count32) begin
if (Count32 == 5'b00000)
Load_EN = 1'b1;
else
Load_EN = 1'b0;
end
always @(state) begin
Mux_Sel2 = state;
end
//
always @(posedge RST or FS or negedge CLK2M) begin
if (RST)
state = 1'b0;
else if (FS == 1'b0)
state = 1'b0;
else if (Count32 == 5'b00000)
state = 1'b1;
end
//
// always @(Load_EN) begin
// Mux_Sel2 = Load_EN;
// end
always @(posedge RST or posedge CLK2M) begin
if (RST)
THR = 8'b00000000;
else if ((CS == 1'b0&&WR == 1'b0&&A0 == 1'b0))
THR = D;
end
//THR_Status是一个状态信号,由于该状态最终可以被处理器通过总线读取,因此可以将它设置为
//8bits,当THR中有数据时为00000001,否则为00000000;
// 该信号应该是寄存器信号,无论THR中是否有数据,只要TSR发出一个读信号,那么在
// 读信号有效的下一个时钟沿,该信号为空
always @(posedge RST or posedge CLK2M) begin
if (RST)
THR_Status = 8'b00000001;
else if (WR == 1'b0 && THR_Read == 1'b1 &&A0 == 1'b0)
THR_Status = 8'b00000001;
else if (WR == 1'b1 && THR_Read == 1'b0)
THR_Status = 8'b00000000;
end
always @(Count8) begin
if (Count8 == 3'b000)
THR_Read = 1'b0;//THR_Read 0 有效
else
THR_Read = 1'b1;
end
always @(posedge RST or posedge CLK2M) begin
if (RST)
Count8 = 3'b000;
else if (~FS)
Count8 = 3'b111;
else if (TSR_Shift_EN)
Count8 = Count8 - 1;
end
assign D = (RD == 1'b0 && A0 == 1'b0)?THR_Status:8'bzzzzzzzz;
endmodule
- 仿真文件
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/12 18:19:03
// Design Name:
// Module Name: tb_p2s
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_p2s;
// Inputs
reg RST, CLK2M, CS, WR, RD, FS, A0;
wire [7:0] D;
// Outputs
wire TxD;
// Instantiate the module
p2s_transmitter p2s_inst (
.RST(RST),
.CLK2M(CLK2M),
.CS(CS),
.WR(WR),
.RD(RD),
.FS(FS),
.A0(A0),
.D(D),
.TxD(TxD)
);
reg [7:0] d;
// Clock generation
parameter CLK2M_period = 244; // 2.048MHz clock
always begin
#CLK2M_period CLK2M = ~CLK2M;
end
initial begin
#122;
#488;
FS = 1'b0;
//CS = 1'b0;
#244;
FS = 1'b1;
//CS = 1'b1;
//#12256;
#20000;
#500;
#244;
FS = 1'b0;
#244;
FS = 1'b1;
#20000;
end
//assign D = (RD == 1'b0)?d:8'bz;、
assign D = d;
initial begin
d = 8'b1100_1100;
# 732;//244+488
# 2928;//488*8
//CS = 1'b0;
d = 8'b1111_1000;
# 3904;//488*8
d = 8'b1111_1011;
# 3904;//488*8
d = 8'b1110_1000;
# 3904;//488*8
d = 8'b1110_1110;
# 732;//244+488
# 2928;//488*8
//CS = 1'b0;
d = 8'b1111_1000;
# 3904;//488*8
d = 8'b1111_1011;
# 3904;//488*8
d = 8'b1110_1000;
# 3904;//488*8
d = 8'b1110_1110;
end
initial begin
CS = 1'b1;
#122;
CS = 1'b0;
#244;
CS = 1'b1;
RD = 1'b0;
#3660;
CS = 1'b0;
RD = 1'b1;
#244;
CS = 1'b1;
RD = 1'b0;
while (1) begin
#4148;
CS = 1'b0;
RD = 1'b1;
#244;
CS = 1'b1;
RD = 1'b0;
end
end
// Testbench stimulus
initial begin
// Initialize inputs
RST = 1'b0;
//CS = 1'b1;
WR = 1'b0;
RD = 1'b1;
FS = 1'b1;
CLK2M = 1'b0;
A0 = 1'b0;
#22;
RST = 1'b1;
#50;
RST = 1'b0;
#10;
//CS = 1'b0;
#244 ;
//CS = 1'b1;
#2440;
#24400;
WR = 1'b1;
d = 8'b1111_1000;
#24400;
WR = 1'b0;
#20000;
// End simulation
$finish;
end
endmodule
- 仿真成功图:
图 1 初始化仿真图
因为THR中的数据是由CS,RD和A0信号等待时钟上升沿控制,而将THR中的数据打入TSR中也需要时钟上升沿,故延迟一个时钟周期才将FS置零。
图 2 四个字节成功传输概览图
图 3五个连续‘1’之后插入‘0’
图 4相邻两个字节满足五个连续的‘1’并没有插入‘0’
图 5成功进入空闲状态,并成功传输7EH
图 6成功退出空闲状态,进入下一个阶段的传输