用Verilog 实现一个帧结构的串行数据接收器;
- 串行数据输入为:NRZ数据加位时钟(BCL)格式,高位在前
帧结构为:8位构成一个字,64字构成一个帧。每帧的第一个字为同步字。 - 同步字图案存储在可由CPU读写的同步字寄存器(端口地址00H)中
串行接受器在连续检测到3个同步图案后,开始接受数据,并向CPU中传送数据。串行数据接收器每接收到一个字,先送到数据寄存器中, CPU以I/O读方式,从数据寄存器中读取数据(端口地址为01H) - 若数据寄存器已满,再有数据写入时,则覆盖原有的数据。在数据寄存器为空时,CPU从数据寄存器中读到的数据将是同步字寄存器的内容。
在接收数据过程中,若任何一帧的同步字不匹配,则进入到头步状态,停止数据接收。失步后,必须重新同步(连续检测到3个同步图案),然后开始新的数据接收。 - 寄存器的读写采用和8031类似的控制方式,有关信号包括:双向数据(DATA[7:0])、I/O地址(ADDR[7:0])、I/O写(IOW)、和I/O读(IOR),其中IOW和IOR都是低电平有效
- 设计者可以根据需要增加其它的输入输出信号
设计分析
-
端口
-
输入数据和时钟之间的关系
-
帧结构
8位构成一个字,64字构成一个帧。每帧的第一个字为同步字。
连续检测到三个同步帧,即连续三个同步头和同步图案一样的帧,才开始进行数据接收
-
详细设计-工作阶段非常明显
- 失步阶段:检测同步头,根据情况确定是否转入同步状态
- 同步阶段:检测同步头,如果匹配则接收数据,仍然处于同步阶段;否则转入失步状态。
- 注意:是失步态下检测的一个同步字时需要每个时钟周期都要进行比较
-
实现思路:采用状态机进行实现
- 状态转换的控制
- 计数器:接收位计数-->字,字计数-->数据帧
- 比较器:接收数据与同步字的比较
- 状态转换的控制
-
两个状态
该状态转换关系从功能需求很容易得出
难点:需要检测三个连续的同步帧才能从失步态到同步态
控制不清晰
-
四状态划分
实现难点:每个帧持续8*64个时钟周期,其中第8个时钟周期结束需要同步头比较,后面的504个时钟周期接收数据(同步态)或空等(失步态)
-
8个状态
- 在每个状态,省略了自身状态转移的情况
- 每个R_Headx状态持续八个周期(Read_Head1)除外
- 每个R_Datax状态持续504个周期
- 需要设计一个记8和一个记504的计数器辅助进行控制
-
代码部分
完整代码
csharp
`timescale 1ns / 1ps
module S2P (
reset,clk,serial_in,ior,iow,address,data,cnt
);
input reset;
input clk;
input serial_in;
input ior;
input iow;
input [7:0] address;
inout [7:0] data;
output cnt;
reg [7:0] Data;
reg counter8_en,counter504_en,counter8_clr,counter504_clr;
reg [2:0] counter8;
reg [8:0] counter504;
reg [7:0] shifter,data_reg,sync_word;
reg [2:0] pres_state,next_state;
reg cnt;
parameter R_Head1 = 3'b000,R_Data1 = 3'b001,R_Head2 = 3'b010,R_Data2 = 3'b011,R_Head3 = 3'b100,R_Data3 = 3'b101,R_Head = 3'b110,R_Data = 3'b111;
//状态机
always @(posedge reset or posedge clk) begin
if (reset)
pres_state = R_Head1;
else
pres_state = next_state;
end
always @(pres_state,shifter,counter8,counter504) begin
case(pres_state)
R_Head1: if (shifter == sync_word) next_state = R_Data1;
else next_state = R_Head1;
R_Data1: if (counter504 == 9'b0) next_state = R_Head2;
else next_state = R_Data1;
R_Head2: if (counter8 == 3'b0) begin
if (shifter == sync_word) next_state = R_Data2;
else next_state = R_Head1;
end else next_state = R_Head2;
R_Data2: if (counter504 == 9'b0) next_state = R_Head3;
else next_state = R_Data2;
R_Head3: if (counter8 == 3'b0) begin
if (shifter == sync_word) next_state = R_Data3;
else next_state = R_Head1;
end else next_state = R_Head3;
R_Data3: if (counter504 == 9'b0) next_state = R_Head;
else next_state = R_Data3;
R_Head: if (counter8 == 3'b0) begin
if (shifter == sync_word) next_state = R_Data;
else next_state = R_Head1;
end else next_state = R_Head;
R_Data: if (counter504 == 9'b0) next_state = R_Head;
else next_state = R_Data;
default: next_state = R_Head1;
endcase
end
always @(next_state,pres_state) begin
if (pres_state == R_Data)
cnt = 1'b1;
else cnt = 1'b0;
end
// 移位器和计数器
always @(posedge reset or posedge clk) begin
if (reset)
shifter = 8'b0;
else
shifter = {serial_in,shifter[7:1]};
end
always @(posedge clk or posedge reset) begin
if (reset) counter8 = 3'b111;
else begin
if (counter8_clr) counter8 = 3'b111;
else if (counter8_en)
counter8 = counter8 - 1;
end
end
always @(posedge clk or posedge reset) begin
if (reset) counter504 = 9'b1_1111_0111;
else begin
if (counter504_clr) counter504= 9'b1_1111_0111;
else if (counter504_en)
counter504 = counter504 - 1;
end
end
// 计数器计数使能和清零信号生成
always @(pres_state) begin
if (pres_state == R_Data1 || pres_state == R_Data2 || pres_state == R_Data3 || pres_state == R_Data)
counter8_clr = 1'b1;
else
counter8_clr = 1'b0;
end
always @(pres_state) begin
if (pres_state == R_Head2 || pres_state == R_Head3 || pres_state == R_Head)
counter8_en = 1'b1;
else
counter8_en = 1'b0;
end
always @(pres_state) begin
if (pres_state == R_Head1 || pres_state == R_Head2 || pres_state == R_Head3 || pres_state == R_Head)
counter504_clr = 1'b1;
else
counter504_clr = 1'b0;
end
always @(pres_state) begin
if (pres_state == R_Data1 || pres_state == R_Data2 || pres_state == R_Data3 || pres_state == R_Data)
counter504_en = 1'b1;
else
counter504_en = 1'b0;
end
//数据寄存器读写和同步字寄存器的写入
always @(posedge reset or posedge clk)
if (reset) data_reg = 8'b0;
else if (counter504_en == 1'b1 && counter504[2:0] == 3'b0)
data_reg = shifter;
always @(posedge reset or posedge clk) begin
if (reset)
sync_word = 8'b0000_0001;
else if (iow == 1'b0 && address == 8'b0)
sync_word = data;
end
always @(ior or address or data_reg)
if (ior == 1'b0 && address == 8'b1)
Data = data_reg;
else Data = 8'bz;
assign data = Data;
endmodule
- Testbench
csharp
`timescale 1ns / 1ps
module tb_S2P;
reg reset;
reg clk;
reg serial_in;
reg ior;
reg iow;
reg [7:0] address;
wire[7:0] data;
wire cnt;
S2P s2p (
.reset(reset),
.clk(clk),
.serial_in(serial_in),
.ior(ior),
.iow(iow),
.address(address),
.data(data),
.cnt(cnt)
);
reg [7:0] d;
assign data = (ior == 1'b1)?d:8'bz;
//assign data = (ior == 1'b0 &&address == 8'b1)?d:8'bz;
always begin
#10 clk = ~clk;
end
integer i;
initial begin
serial_in = 1'bZ;
#10 ;
while (1) begin
ior = 1'b1;
#20; serial_in = 1'b0;
#20; serial_in = 1'b1;
#20; serial_in = 1'b1;
#20; serial_in = 1'b1;
#20; serial_in = 1'b1;
#20; serial_in = 1'b1;
#20; serial_in = 1'b1;
#20; serial_in = 1'b0;
for (i = 1;i <= 63*8;i = i + 1)
#20 serial_in = {$random}%2;
end
end
initial begin
clk = 0;
reset = 1'b1;
#20
#5 reset = 1'b0;
end
initial begin
ior = 1'b1;
//address = 8'b0;
//s2p.pres_state = 3'b1;
iow = 1'b1;
address = 8'b0;
d = 8'b01111110;
#20
#5;
iow = 1'b0;
//address = 8'b0;
#10;
address = 8'b0000_0001;
//ior = 1'b0;
#5
iow = 1'b1;
end
always @(cnt) begin
ior = ~cnt;
end
initial begin
#20
#101000;
$finish;
end
endmodule
- 仿真结果