差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rs-232 [2017/03/26 19:34]
gongyu
rs-232 [2017/03/26 20:00] (当前版本)
gongyu [应用案例]
行 160: 行 160:
 One last implementation issue: the "​BaudGeneratorInc"​ calculation is wrong, due to the fact that Verilog uses 32 bits intermediate results, and the calculation exceeds that. Change the line as follow for a workaround. One last implementation issue: the "​BaudGeneratorInc"​ calculation is wrong, due to the fact that Verilog uses 32 bits intermediate results, and the calculation exceeds that. Change the line as follow for a workaround.
  
 +<code verilog>
 parameter BaudGeneratorInc = ((Baud<<​(BaudGeneratorAccWidth-4))+(ClkFrequency>>​5))/​(ClkFrequency>>​4);​ parameter BaudGeneratorInc = ((Baud<<​(BaudGeneratorAccWidth-4))+(ClkFrequency>>​5))/​(ClkFrequency>>​4);​
 +</​code>​
  
 This line has also the added advantage to round the result instead of truncating. This line has also the added advantage to round the result instead of truncating.
行 166: 行 168:
 Now that we have a precise enough Baud generator, we can go ahead with the RS-232 transmitter and receiver modules. Now that we have a precise enough Baud generator, we can go ahead with the RS-232 transmitter and receiver modules.
  
-==== 发送器 ==== +====== 发送器 ​====== 
-==== 接收器 ==== +We are building an "async transmitter"​ with fixed parameters: 8 data bits, 2 stop bits, no-parity. 
-==== 应用案例 ====+{{ :​serialtxdmodule.gif |}} 
 + 
 +It works like that: 
 + 
 +  * The transmitter takes an 8-bits data inside the FPGA and serializes it (starting when the "​TxD_start"​ signal is asserted). 
 +  * The "​busy"​ signal is asserted while a transmission occurs (the "​TxD_start"​ signal is ignored during that time). 
 + 
 +===== Serializing the data ===== 
 + 
 +To go through the start bit, the 8 data bits, and the stop bits, a state machine seems appropriate. 
 +<code verilog>​ 
 +reg [3:0] state; 
 + 
 +// the state machine starts when "​TxD_start"​ is asserted, but advances when "​BaudTick"​ is asserted (115200 times a second) 
 +always @(posedge clk) 
 +case(state) 
 +  4'​b0000:​ if(TxD_start) state <= 4'​b0100;​ 
 +  4'​b0100:​ if(BaudTick) state <= 4'​b1000;​ // start 
 +  4'​b1000:​ if(BaudTick) state <= 4'​b1001;​ // bit 0 
 +  4'​b1001:​ if(BaudTick) state <= 4'​b1010;​ // bit 1 
 +  4'​b1010:​ if(BaudTick) state <= 4'​b1011;​ // bit 2 
 +  4'​b1011:​ if(BaudTick) state <= 4'​b1100;​ // bit 3 
 +  4'​b1100:​ if(BaudTick) state <= 4'​b1101;​ // bit 4 
 +  4'​b1101:​ if(BaudTick) state <= 4'​b1110;​ // bit 5 
 +  4'​b1110:​ if(BaudTick) state <= 4'​b1111;​ // bit 6 
 +  4'​b1111:​ if(BaudTick) state <= 4'​b0001;​ // bit 7 
 +  4'​b0001:​ if(BaudTick) state <= 4'​b0010;​ // stop1 
 +  4'​b0010:​ if(BaudTick) state <= 4'​b0000;​ // stop2 
 +  default: if(BaudTick) state <= 4'​b0000;​ 
 +endcase 
 +</​code>​ 
 + 
 +Now, we just need to generate the "​TxD"​ output. 
 + 
 +<code verilog>​ 
 +reg muxbit; 
 + 
 +always @(state[2:​0]) 
 +case(state[2:​0]) 
 +  0: muxbit <= TxD_data[0];​ 
 +  1: muxbit <= TxD_data[1];​ 
 +  2: muxbit <= TxD_data[2];​ 
 +  3: muxbit <= TxD_data[3];​ 
 +  4: muxbit <= TxD_data[4];​ 
 +  5: muxbit <= TxD_data[5];​ 
 +  6: muxbit <= TxD_data[6];​ 
 +  7: muxbit <= TxD_data[7];​ 
 +endcase 
 + 
 +// combine start, data, and stop bits together 
 +assign TxD = (state<​4) | (state[3] & muxbit); 
 + 
 +</​code>​ 
 + 
 +====== 接收器 ​====== 
 +We are building an "async receiver":​ 
 + 
 +{{ :​serialrxdmodule.gif |}} 
 + 
 +Our implementation works like that: 
 + 
 +  * The module assembles data from the RxD line as it comes. 
 +  * As a byte is being received, it appears on the "​data"​ bus. Once a complete byte has been received, "​data_ready"​ is asserted for one clock. 
 + 
 +Note that "​data"​ is valid only when "​data_ready"​ is asserted. The rest of the time, don't use it as new data may come that shuffles it. 
 + 
 +===== Oversampling ===== 
 + 
 +An asynchronous receiver has to somehow get in-sync with the incoming signal (it normally doesn'​t have access to the clock used by the transmitter). 
 + 
 +To determine when a new data byte is coming, we look for the "​start"​ bit by oversampling the signal at a multiple of the baud rate frequency. 
 +Once the "​start"​ bit is detected, we sample the line at the known baud rate to acquire the data bits. 
 +Receivers typically oversample the incoming signal at 16 times the baud rate. We use 8 times here... For 115200 bauds, that gives a sampling rate of 921600Hz. 
 + 
 +Let's assume that we have a "​Baud8Tick"​ signal available, asserted 921600 times a second. 
 + 
 +===== The design ===== 
 + 
 +First, the incoming "​RxD"​ signal has no relationship with our clock. 
 +We use two D flip-flops to oversample it, and synchronize it to our clock domain. 
 + 
 +<code verilog>​ 
 +reg [1:0] RxD_sync; 
 +always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0],​ RxD}; 
 +</​code>​ 
 + 
 +We filter the data, so that short spikes on the RxD line aren't mistaken with start bits. 
 +<code verilog>​ 
 +reg [1:0] RxD_cnt; 
 +reg RxD_bit; 
 + 
 +always @(posedge clk) 
 +if(Baud8Tick) 
 +begin 
 +  if(RxD_sync[1] && RxD_cnt!=2'​b11) RxD_cnt <= RxD_cnt + 1; 
 +  else  
 +  if(~RxD_sync[1] && RxD_cnt!=2'​b00) RxD_cnt <= RxD_cnt - 1; 
 + 
 +  if(RxD_cnt==2'​b00) RxD_bit <= 0; 
 +  else 
 +  if(RxD_cnt==2'​b11) RxD_bit <= 1; 
 +end 
 +</​code>​ 
 + 
 +A state machine allows us to go through each bit received, once a "​start"​ is detected. 
 +<code verilog>​ 
 +reg [3:0] state; 
 + 
 +always @(posedge clk) 
 +if(Baud8Tick) 
 +case(state) 
 +  4'​b0000:​ if(~RxD_bit) state <= 4'​b1000;​ // start bit found? 
 +  4'​b1000:​ if(next_bit) state <= 4'​b1001;​ // bit 0 
 +  4'​b1001:​ if(next_bit) state <= 4'​b1010;​ // bit 1 
 +  4'​b1010:​ if(next_bit) state <= 4'​b1011;​ // bit 2 
 +  4'​b1011:​ if(next_bit) state <= 4'​b1100;​ // bit 3 
 +  4'​b1100:​ if(next_bit) state <= 4'​b1101;​ // bit 4 
 +  4'​b1101:​ if(next_bit) state <= 4'​b1110;​ // bit 5 
 +  4'​b1110:​ if(next_bit) state <= 4'​b1111;​ // bit 6 
 +  4'​b1111:​ if(next_bit) state <= 4'​b0001;​ // bit 7 
 +  4'​b0001:​ if(next_bit) state <= 4'​b0000;​ // stop bit 
 +  default: state <= 4'​b0000;​ 
 +endcase 
 +</​code>​ 
 + 
 +Notice that we used a "​next_bit"​ signal, to go from bit to bit. 
 +<code verilog>​ 
 +reg [2:0] bit_spacing;​ 
 + 
 +always @(posedge clk) 
 +if(state==0) 
 +  bit_spacing <= 0; 
 +else 
 +if(Baud8Tick) 
 +  bit_spacing <= bit_spacing + 1; 
 + 
 +wire next_bit = (bit_spacing==7);​ 
 +Finally a shift register collects the data bits as they come. 
 + 
 +reg [7:0] RxD_data; 
 +always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:​1]};​ 
 +</​code>​ 
 + 
 + 
 +====== 应用案例 ====== 
 +This design allows controlling a few FPGA pins from your PC (through your PC's serial port). 
 + 
 +  * It create 8 outputs on the FPGA (port named "​GPout"​). GPout is updated by any character that the FPGA receives. 
 +  * Also 8 inputs on the FPGA (port named "​GPin"​). GPin is transmitted every time the FPGA receives a character. 
 + 
 +The GP outputs can be used to control anything remotely from your PC, might be LEDs or a coffee machine... 
 +<code verilog>​ 
 +module serialGPIO( 
 +    input clk, 
 +    input RxD, 
 +    output TxD, 
 + 
 +    output reg [7:0] GPout, ​ // general purpose outputs 
 +    input [7:0] GPin  // general purpose inputs 
 +); 
 + 
 +wire RxD_data_ready;​ 
 +wire [7:0] RxD_data; 
 +async_receiver RX(.clk(clk),​ .RxD(RxD), .RxD_data_ready(RxD_data_ready),​ .RxD_data(RxD_data));​ 
 +always @(posedge clk) if(RxD_data_ready) GPout <= RxD_data; 
 + 
 +async_transmitter TX(.clk(clk),​ .TxD(TxD), .TxD_start(RxD_data_ready),​ .TxD_data(GPin));​ 
 +endmodule 
 + 
 +</​code>​ 
 + 
 +Remember to grab the async_receiver and async_transmitter modules here, and to update the clock frequency values inside.