差别

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

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rs-232 [2017/03/26 19:28]
gongyu
rs-232 [2017/03/26 20:00] (当前版本)
gongyu [应用案例]
行 91: 行 91:
 Traditionally,​ RS-232 chips use a 1.8432MHz clock, because that makes generating the standard baud frequencies very easy... 1.8432MHz divided by 16 gives 115200Hz. Traditionally,​ RS-232 chips use a 1.8432MHz clock, because that makes generating the standard baud frequencies very easy... 1.8432MHz divided by 16 gives 115200Hz.
  
 +------
 +<code verilog>
 // let's assume the FPGA clock signal runs at 1.8432MHz // let's assume the FPGA clock signal runs at 1.8432MHz
 // we create a 4-bit counter // we create a 4-bit counter
行 98: 行 100:
 // and a tick signal that is asserted once every 16 clocks (so 115200 times a second) // and a tick signal that is asserted once every 16 clocks (so 115200 times a second)
 wire BaudTick = (BaudDivCnt==15);​ wire BaudTick = (BaudDivCnt==15);​
 +</​code>​
 +
 That was easy. But what do you do if instead of 1.8432MHz, you have a 2MHz clock? To generate 115200Hz from a 2MHz clock, we need to divide the clock by "​17.361111111..."​ Not exactly a round number. The solution is to divide sometimes by 17, sometimes by 18, making sure the ratio stays "​17.361111111"​. That's actually easy to do. That was easy. But what do you do if instead of 1.8432MHz, you have a 2MHz clock? To generate 115200Hz from a 2MHz clock, we need to divide the clock by "​17.361111111..."​ Not exactly a round number. The solution is to divide sometimes by 17, sometimes by 18, making sure the ratio stays "​17.361111111"​. That's actually easy to do.
  
 Look at the following "​C"​ code: Look at the following "​C"​ code:
  
 +<code c>
 while(1) // repeat forever while(1) // repeat forever
 { {
行 109: 行 114:
   acc %= 2000000;   acc %= 2000000;
 } }
 +</​code>​
 +
 That prints the "​*"​ in the exact ratio, once every "​17.361111111..."​ loops on average. That prints the "​*"​ in the exact ratio, once every "​17.361111111..."​ loops on average.
  
行 115: 行 122:
 It is desirable that the 2000000 be a power of two. Obviously 2000000 is not. So we change the ratio... Instead of "​2000000/​115200",​ let's use "​1024/​59"​ = 17.356. That's very close to our ideal ratio, and makes an efficient FPGA implementation:​ we use a 10-bit accumulator incremented by 59, with a tick marked everytime the accumulator overflows. It is desirable that the 2000000 be a power of two. Obviously 2000000 is not. So we change the ratio... Instead of "​2000000/​115200",​ let's use "​1024/​59"​ = 17.356. That's very close to our ideal ratio, and makes an efficient FPGA implementation:​ we use a 10-bit accumulator incremented by 59, with a tick marked everytime the accumulator overflows.
  
 +------
 +<code verilog>
 // let's assume the FPGA clock signal runs at 2.0000MHz // let's assume the FPGA clock signal runs at 2.0000MHz
 // we use a 10-bit accumulator plus an extra bit for the accumulator carry-out // we use a 10-bit accumulator plus an extra bit for the accumulator carry-out
行 124: 行 133:
  
 wire BaudTick = acc[10]; // so that the 11th bit is the accumulator carry-out wire BaudTick = acc[10]; // so that the 11th bit is the accumulator carry-out
 +
 +</​code>​
 +
 Using our 2MHz clock, "​BaudTick"​ is asserted 115234 times a second, a 0.03% error from the ideal 115200. Using our 2MHz clock, "​BaudTick"​ is asserted 115234 times a second, a 0.03% error from the ideal 115200.
  
行 132: 行 144:
 Here's a design with a 25MHz clock and a 16 bits accumulator. The design is parameterized,​ so easy to customize. Here's a design with a 25MHz clock and a 16 bits accumulator. The design is parameterized,​ so easy to customize.
  
 +<code verilog>
 parameter ClkFrequency = 25000000; // 25MHz parameter ClkFrequency = 25000000; // 25MHz
 parameter Baud = 115200; parameter Baud = 115200;
行 142: 行 155:
  
 wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth];​ wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth];​
 +
 +</​code>​
 +
 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.
行 150: 行 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.