帧结构的串行数据接收器——Verilog实现

用Verilog 实现一个帧结构的串行数据接收器;

  1. 串行数据输入为:NRZ数据加位时钟(BCL)格式,高位在前
    帧结构为:8位构成一个字,64字构成一个帧。每帧的第一个字为同步字。
  2. 同步字图案存储在可由CPU读写的同步字寄存器(端口地址00H)中
    串行接受器在连续检测到3个同步图案后,开始接受数据,并向CPU中传送数据。串行数据接收器每接收到一个字,先送到数据寄存器中, CPU以I/O读方式,从数据寄存器中读取数据(端口地址为01H)
  3. 若数据寄存器已满,再有数据写入时,则覆盖原有的数据。在数据寄存器为空时,CPU从数据寄存器中读到的数据将是同步字寄存器的内容。
    在接收数据过程中,若任何一帧的同步字不匹配,则进入到头步状态,停止数据接收。失步后,必须重新同步(连续检测到3个同步图案),然后开始新的数据接收。
  4. 寄存器的读写采用和8031类似的控制方式,有关信号包括:双向数据(DATA[7:0])、I/O地址(ADDR[7:0])、I/O写(IOW)、和I/O读(IOR),其中IOW和IOR都是低电平有效
  5. 设计者可以根据需要增加其它的输入输出信号

设计分析

  • 端口

  • 输入数据和时钟之间的关系

  • 帧结构

    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
  • 仿真结果
相关推荐
北城笑笑4 小时前
FPGA 14 ,硬件开发板分类详解,FPGA开发板与普通开发板烧录的区别
fpga开发·fpga
2202_754421544 小时前
一个计算频率的模块
驱动开发·fpga开发
小灰灰的FPGA5 小时前
低速接口项目之串口Uart开发(七)——如何在FPGA项目中实现自适应波特率串口功能
fpga开发
fei_sun1 天前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto1 天前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安1 天前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈1 天前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun1 天前
【Verilog】第二章作业
fpga开发·verilog
碎碎思1 天前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则
江山如画,佳人北望1 天前
fpga-状态机的设计及应用
fpga开发