Menu Close

曼彻斯特编码-参考代码(Verilog)

根据曼彻斯特码的编解码原理,使用verilog 语言设计曼彻斯特编码的PHY层逻辑。

相关的参考文章:

曼彻斯特编码介绍

曼彻斯特解码介绍

曼彻斯特解码-参考代码(Verilog)

 

本工程采用mvb 相关曼码通讯协议,其他曼码工程与之类似。 一般情况下,只需要修改起始定界符,终止定界符就可以应用到其他的曼码工程中去了。

曼码设计框图:

%title插图%num

整个曼码模块由 phy_mcode.v phy_bt.v mcode_tx_phy.v mcode_rx_phy 四个文件组成。

顶层文件:phy_mcode.v 接口包括

clk : 系统时钟, 当前使用的系统时钟为48M 时钟,mvb 的通讯速率是1.5M, 48M 系统时钟会非常方便分频。

reset: 对整个曼码模块进行复位。

mcode_tx: 曼码发送pin ,可连接到fpga pin 上 ,用于曼码的发送。

mcode_tx_de:曼码发送enable pin, 可连接到fpga pin 上, 用于曼码发送。(有时硬件设计会采用rs485,rs422等通讯方式,这时就需要这个pin 来控制) 不是必须的,根据用户的硬件方案选择使用。 这个信号会包含mcode_tx 信号,宽度(时间轴)会大于 mode_tx, 用来防止数据传输时丢失。

%title插图%num

mcode_rx: 曼码接收到的串行数据。

mcode_rx_de: 曼码接收enable 线。 使用方法和发送类似。

tx_data: 8bit 数据 输入, 由fpga 内容逻辑产生,将这个数据发送给曼码模块, 曼码模块将数据以曼彻斯特编码方式串行输出的fpga pin 上。

tx_data_en: tx_data 的enable, 通知曼码模块,当前的tx_data 可以使用了。(48M单时钟周期)

tx_start_en: 通知曼码模块,发送起始定界符(48M单时钟周期)

tx_stop_en: 通知曼码模块,发送终止定界符(48M单时钟周期)

tx_busy: 在曼码数据发送期间(包括发送 起始定界符,终止定界符,正常的数据发送)一直为高电平,当发送结束,tx_busy 为低电平。这时fpga 系统软件可以发送下一个指令了(起始定界符,终止定界符,数据)。

rx_data: 从曼码phy 层得到的数据(将曼码解码得到的正常数据,可用于fpga 系统使用)

rx_rdy: 表示rx_data 已经正常接收到了(48M单时钟周期)

 

rx_eop: 表示 当前曼码包已经结束了(48M单时钟周期)

rx_data_err: 表示 当前曼码接收到的数据由错误。

rx_start_err: 表述 当前曼码接收的起始定界符错误。

 

顶层文件phy_mcode.v 代码:

module phy_mcode(
    input  clk,
// 485 pin
    output mcode_tx,
    output mcode_tx_de,
 
    input  mcode_rx,
    output mcode_rx_de,
// send port    
    input  [7:0] tx_data,
    input  tx_data_en,
 
    input  tx_start_en,
    input  tx_stop_en,
     
    output tx_busy,
// rev port
    output [7:0] rx_data,
    output rx_rdy,
    output rx_eop,
 
    output rx_data_err,
    output rx_start_err,
     
    input  reset
);
 
wire bt_3m_tick;
 
phy_bt  phy_bt_inst
(
    .clk        (clk),  //48m
    .clk_div    (15),   // divide 16
    .bt_3m_tick (bt_3m_tick),
    .reset      (reset)
);    
 
mcode_tx_phy   mcode_tx_phy_inst
(
    .clk        (clk),
    .bt_tick    (bt_3m_tick),
 
    .mcode_tx   (mcode_tx),
    .mcode_tx_de(mcode_tx_de),
 
    .tx_data    (tx_data),
    .tx_data_en (tx_data_en),
 
    .tx_start_en(tx_start_en),
    .tx_stop_en (tx_stop_en),
 
    .tx_busy    (tx_busy),
 
    .reset      (reset)
);
//assign mcode_rx_de = ~mcode_tx_de;    
assign mcode_rx_de = mcode_tx_de;
mcode_rx_phy  mcode_rx_phy_inst
(
    .clk        (clk),
 
    .mcode_rx   (mcode_rx),
    .mcode_rx_de(mcode_rx_de),
 
    .rx_data    (rx_data),
    .rx_rdy     (rx_rdy),
    .rx_eop     (rx_eop),
 
    .rx_data_err(rx_data_err),
    .rx_start_err(rx_start_err),    
    .reset      (reset)
);
     
endmodule

注意:
//assign mcode_rx_de = ~mcode_tx_de;
assign mcode_rx_de = mcode_tx_de;
当前的工程是一个测试代码,也就是说发送的数据会被接收模块(mcode_rx_phy.v)立即接收到。对于mcode_rx_phy来说,
mcode_rx_de 是一个input 信号。这样方便在一个板子上做测试。但是在实际的工程中, 由于外部接口(硬件设计)有所不同,用户可以根据情况修改这个信号。


举例:在串行异步半双工通讯过程中, 往往是发送完数据后, 转为接收状态,接收完数据后(完整的接收包)后再次转为发送状态。(一般情况下的异步通讯)。
但是也不排除用户使用的是全双工的通讯方式, 用户在具体的使用(用户自己的项目中),可以根据情况,修改mcode_rx_de 的状态, 甚至可以修改input ,output等
状态来满足用户自己的需求。本文中mcode_rx_phy.v 只有在mcode_rx_de = 1 时,才接收曼码数据。

phy_bt.v 文件:
根据系统时钟分频,产生0.5BT 的波特率,用于曼码发送使用。 当前文件依照mvb 协议 产生3M (0.5BT) 波特率,其他的曼码协议,用户可以自行更改。
clk: 系统时钟 (48M)
clk_div:
分频系数, 本文中选择15. 3M = 48M / (15 + 1)
bt_3m_tick:
0.5BT 信号, 这时一个与48M 时钟同步的单沿信号,频率是3M
reset:
系统复位信号

用户可以根据自己协议的需要,更改相关的系统时钟和分频系数来满足客户的需求,本文是以mvb 为例的。

phy_bt.v 代码

module phy_bt(
    input  clk,
    input  [7:0] clk_div,
    output reg bt_3m_tick = 0,
    input  reset
);
     
reg [7:0] cnt = 0;
always @ (posedge clk) // 48m
if(reset) 
begin
    bt_3m_tick <= 0;
    cnt <= 0;
end
else 
begin
    if(cnt == clk_div) 
    begin
        cnt <= 0;
        bt_3m_tick <= 1;
    end
    else 
    begin
        bt_3m_tick <= 0;
        cnt <= cnt + 1;
    end
end
 
 
     
     
endmodule

 

mcode_tx_phy.v 文件:

接口信号

clk:  系统时钟 (48M)

bt_tick: 0.5BT 波特率(3M 频率, 48M时钟下的单沿信号)

mcode_tx: 定义与phy_mcode.v(上文中)的定义相同

mcode_tx_de: 定义与phy_mcode.v(上文中)的定义相同

tx_data: 定义与phy_mcode.v(上文中)的定义相同

tx_data_en: 定义与phy_mcode.v(上文中)的定义相同

tx_start_en: 定义与phy_mcode.v(上文中)的定义相同

tx_stop_en: 定义与phy_mcode.v(上文中)的定义相同

tx_busy: 定义与phy_mcode.v(上文中)的定义相同

reset: 模块复位信号

mcode_tx_phy.v 功能的使用流程:

  1. fpga 系统 给出 tx_start_en  (单沿信号) ,通知tx phy(mcode_tx_phy.v) 产生起始定界符
  2. 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
  3. 等待tx_busy 信号为零, 发送tx_data 数据,同时产生tx_data_en(单沿信号)
  4. 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
  5. 如果还有要发送的数据,跳转到3; 如果没有数据需要发送,等待tx_busy信号为零, 发送tx_stop_en(单沿信号)
  6. 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
  7. 等待tx_busy 信号为零, 跳转到1 

到此一个完整的曼码包发送完毕。

mcode_tx_phy.v 代码:

module mcode_tx_phy(
    input  clk,
    input  bt_tick,
 
    output reg mcode_tx = 1,
    output reg mcode_tx_de = 0,
 
    input  [7:0] tx_data,
    input  tx_data_en,
 
    input  tx_start_en,
    input  tx_stop_en,
     
    output reg tx_busy = 0,
     
    input  reset
);
 
wire [17:0] start_flag = {2'b10, 2'b10, 2'b10, 2'b10, 2'b00, 2'b11, 2'b10, 2'b00, 2'b11};
wire [3:0]  stop_flag = {2'b00, 2'b11};
//-----------------------------------------------------------------
reg [15:0] temp_data = 0;
reg [2:0] tx_st = 0;
reg [7:0] tx_cnt = 0;
always @ (posedge clk)
if(reset)
begin
    mcode_tx_de <= 0;
    mcode_tx <= 1;
    tx_st <= 0;
end
else case(tx_st)
0:
begin
    tx_cnt <= 0;
    tx_busy <= 0;
     
    if(tx_start_en)
    begin
        tx_busy <= 1;
        mcode_tx_de <= 1;
        tx_st <= 1;
    end
    else
        mcode_tx_de <= 0;
 
end
1:
begin
    if(&tx_cnt)
    begin
        tx_cnt <= 17;
        tx_st <= 2;
    end
    else tx_cnt <= tx_cnt + 1;
end
2: // start delimiter
begin
    if(bt_tick)
    begin
        mcode_tx <= start_flag[tx_cnt];
        tx_cnt <= tx_cnt - 1;
        if(tx_cnt == 0)
        begin
            tx_busy <= 0;
            tx_cnt <= 15;
            tx_st <= 3;
        end
    end
end
3:
begin
    tx_busy <= 0;
     
    if(tx_stop_en)
    begin
        tx_busy <= 1;
        tx_cnt <= 3;
        tx_st <= 5;
    end
    else if(tx_data_en)
    begin
        temp_data[15:8] <= {tx_data[7],~tx_data[7], tx_data[6],~tx_data[6], tx_data[5],~tx_data[5], tx_data[4],~tx_data[4] };
        temp_data[7:0]  <= {tx_data[3],~tx_data[3], tx_data[2],~tx_data[2], tx_data[1],~tx_data[1], tx_data[0],~tx_data[0] };
 
        tx_busy <= 1;    
        tx_cnt <= 15;
        tx_st <= 4;
    end
     
end
4:
begin
    if(bt_tick)
    begin
        mcode_tx <= temp_data[tx_cnt];
         
        if(tx_cnt == 0) tx_st <= 3;
        else  tx_cnt <= tx_cnt - 1;
    end
end
5:
begin
    if(bt_tick)
    begin
        mcode_tx <= stop_flag[tx_cnt];
         
        if(tx_cnt == 0) tx_st <= 6;
        else tx_cnt <= tx_cnt - 1;
    end
end
6:
begin
    if(&tx_cnt)
    begin
        tx_busy <= 0;
        tx_cnt <= 0;
        tx_st <= 0;
    end
    else tx_cnt <= tx_cnt + 1;
end
default:
begin
    tx_st <= 0;
end
endcase
 
//-----------------------------------------------------------------
     
endmodule

 

其中:

wire [17:0] start_flag = {2’b10, 2’b10, 2’b10, 2’b10, 2’b00, 2’b11, 2’b10, 2’b00, 2’b11};   //起始定界符

wire [3:0] stop_flag = {2’b00, 2’b11};  //终止定界符

 

在tx_st 状态机中 :

状态0: 等待tx_start_en 信号,得到tx_start_en 信号后, 跳转到状态1

状态1: 产生mcode_tx_de = 1 , 但没有发送起始定界符,让长线传输中,线路上信号稳定一段时间(&cnt == 1) 256 个系统时钟周期,这里是测试例子,实际需要多少时间,用户可以根据协议修改。如果用户的协议中不需要mcode_tx_de 这个信号, 状态1 可以之间跳转到状态2

状态2: 发送起始定界符18bit (9对 数据 )

状态3: 根据情况准备发送数据还是发送终止定界符。 发送数据,将数据编码为曼码格式 ,跳转到状态4; 发送终止定界符,跳转到状态5

状态4: 发送数据,发送完成后跳转到状态3

状态5: 发送终止定界符,发送完成后,跳转到状态6

状态6: 延迟一段时间后,mcode_tx_de  = 0; 返回状态0

 

仿真波形为:

%title插图%num

1: 发送tx_start_en

2: 发送数据1

3: 发送数据2

4: 发送数据3

5: 发送 tx_stop_en

mcode_tx 上显示的是完整一包的曼码波形。

 

Posted in FPGA, FPGA, IP开发, RISC-V, RISC-V 外设, Verilog, 教材与教案, 文章

发表评论

相关链接