根据曼彻斯特码的编解码原理,使用verilog 语言设计曼彻斯特编码的PHY层逻辑。
相关的参考文章:
本工程采用mvb 相关曼码通讯协议,其他曼码工程与之类似。 一般情况下,只需要修改起始定界符,终止定界符就可以应用到其他的曼码工程中去了。
曼码设计框图:

整个曼码模块由 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, 用来防止数据传输时丢失。

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 代码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
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 [/code] |
注意:
//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 代码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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 功能的使用流程:
- fpga 系统 给出 tx_start_en (单沿信号) ,通知tx phy(mcode_tx_phy.v) 产生起始定界符
- 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
- 等待tx_busy 信号为零, 发送tx_data 数据,同时产生tx_data_en(单沿信号)
- 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
- 如果还有要发送的数据,跳转到3; 如果没有数据需要发送,等待tx_busy信号为零, 发送tx_stop_en(单沿信号)
- 等待tx phy (mcode_tx_phy.v) tx_busy 信号 为一
- 等待tx_busy 信号为零, 跳转到1
到此一个完整的曼码包发送完毕。
mcode_tx_phy.v 代码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
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
仿真波形为:

1: 发送tx_start_en
2: 发送数据1
3: 发送数据2
4: 发送数据3
5: 发送 tx_stop_en
mcode_tx 上显示的是完整一包的曼码波形。