龙芯杯个人赛串口——做一个 UART串口——RS-232

文章目录

      • Async transmitter
      • Async receiver
        • 1. RS-232 串行接口的工作原理
          • DB-9 connector
          • Asynchronous communication
          • How fast can we send data?
        • 2.波特率时钟生成器
          • Parameterized FPGA baud generator
        • 3.RS-232 transmitter
          • 数据序列化
          • 完整代码:
        • 4.RS-232 receiver
          • Oversampling
          • The design
          • 完整代码
        • 5.How to use the RS-232 transmitter and receiver
      • 龙芯杯个人赛串口信号连接

串行接口是将 FPGA 连接到 PC 的简单方法。我们只需要一个发射器和接收器模块。

Async transmitter

它通过对要传输的数据进行序列化,创建一个信号 “TxD”。
在这里插入图片描述

Async receiver

它从 FPGA 外部获取信号 “RxD”,并将其 “去序列化”,以便在 FPGA 内部使用。

在这里插入图片描述
本项目包括五个部分

    1. RS-232 串行接口的工作原理
    1. 波特发生器
    1. 发射器
    1. 接收器
    1. 使用示例
1. RS-232 串行接口的工作原理

RS-232 接口具有以下特点:
- 使用 9 针连接器 “DB-9”(老式 PC 使用 25 针连接器 “DB-25”)。
- 允许双向全双工通信(个人电脑可同时发送和接收数据)。
- 最大通信速度约为 10KB/s。

DB-9 connector

可能在电脑背面看到过这个连接器。
在这里插入图片描述
它有 9 个引脚,但重要的有 3 个:

第 2 针:RxD(接收数据)。
第 3 针:TxD(发送数据)。
第 5 针:GND(接地)。

只需 3 根导线,就能发送和接收数据。

数据通常以 8 位为单位发送(我们称之为字节),并进行 “序列化”:先发送 LSB(数据位 0),然后是位 1,…最后是 MSB(位 7)。

Asynchronous communication

该接口使用异步协议。这意味着没有时钟信号与数据一起传输。接收器必须有办法根据接收到的数据位 “计时”。

在 RS-232 的情况下,可以这样做:

电缆两端事先就通信参数(速度、格式......)达成一致。这需要在通信开始前手动完成。
只要线路处于空闲状态,发送器就会发送 "空闲"(="1")。
发送器在传输每个字节前都会发送 "开始"(="0"),以便接收器知道有字节到来。
发送字节数据的 8 位。
发送器在每个字节后发送 "停止"(="1")。

让我们看看 0x55 字节在传输时的样子:
在这里插入图片描述
字节 0x55 的二进制形式是 01010101。
但由于它是先传输 LSB(第 0 位),因此该行会像这样切换: 1-0-1-0-1-0-1-0.

下面是另一个例子:
在这里插入图片描述

How fast can we send data?

速度以波特为单位,即每秒可发送多少比特。例如,1000 波特表示每秒 1000 比特,或每个比特持续一毫秒。

RS-232 接口的常见实现(如 PC 中使用的接口)不允许使用任何速度。如果你想使用 123456 波特,那就没戏了。你必须采用某种 "标准 "速度。常见的值有

1200 波特。
9600 波特。
38400 波特。
115200 波特(通常是最快的速度)。

115200 波特时,每个比特持续 (1/115200) = 8.7µs。如果传输 8 位数据,则持续时间为 8 x 8.7µs = 69µs。但每个字节需要一个额外的起始位和停止位,因此实际上需要 10 x 8.7µs = 87µs。这意味着最高速度为每秒 11.5KB。

在 115200 波特的情况下,一些带有错误芯片的 PC 需要一个 "长 "停止位(1.5 或 2 位长…),这使得最高速度降至每秒 10.5KB 左右。
物理层

电线上的信号使用正/负电压方案。

1 "使用 -10V(或 -5V 至 -15V)电压发送。
0 "使用 +10V(或 5V 至 15V 之间)发送。

因此,空闲线路的电压类似于 -10V。

2.波特率时钟生成器

在这里,我们希望以最大速度使用串行链路,即 115200 波特(较慢的速度也很容易生成)。FPGA 通常以 MHz 速度运行,远高于 115200Hz(按当今标准,RS-232 的速度相当慢)。我们需要找到一种方法,(通过 FPGA 时钟)产生尽可能接近每秒 115200 次的 "滴答 "声。

传统上,RS-232 芯片使用 1.8432MHz 时钟,因为这样可以很容易地产生标准波特频率… 1.8432MHz 除以 16 得到 115200Hz。

// let's assume the FPGA clock signal runs at 1.8432MHz
// we create a 4-bit counter
reg [3:0] BaudDivCnt;
always @(posedge clk) BaudDivCnt <= BaudDivCnt + 1; // count forever from 0 to 15// and a tick signal that is asserted once every 16 clocks (so 115200 times a second)
wire BaudTick = (BaudDivCnt==15); 

这很简单。但如果时钟频率不是 1.8432MHz,而是 2MHz,该怎么办呢?要从 2MHz 时钟产生 115200Hz 的频率,我们需要将时钟除以 “17.361111111…”。这可不是一个整数。解决办法是有时除以 17,有时除以 18,确保比率保持 “17.361111111”。这其实很容易做到。

请看下面的 "C "代码

while(1) // repeat forever
{acc += 115200;if(acc>=2000000) printf("*"); else printf(" ");acc %= 2000000;
}

这样,平均每 "17.361111111… "循环一次,就能以精确的比例打印出 “*”。

要在 FPGA 中高效地实现同样的功能,我们需要依靠串行接口能够容忍波特频率发生器中百分之几的误差。

我们希望 2000000 是 2 的幂。显然,2000000 不是。因此,我们要改变比率… 用 “1024/59” = 17.356 代替 “2000000/115200”。这非常接近我们的理想比率,而且可以高效地在 FPGA 上实现:我们使用一个 10 位累加器,以 59 为增量,每当累加器溢出时打一个勾。

// 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
reg [10:0] acc;   // 11 bits total!// add 59 to the accumulator at each clock
always @(posedge clk)acc <= acc[9:0] + 59; // use 10 bits from the previous accumulator result, but save the full 11 bits resultwire BaudTick = acc[10]; // so that the 11th bit is the accumulator carry-out 

使用我们的 2MHz 时钟,"BaudTick "每秒断言 115234 次,与理想的 115200 误差为 0.03%。

Parameterized FPGA baud generator

之前的设计使用的是 10 位累加器,但随着时钟频率的增加,需要更多的位数。
下面是一个使用 25MHz 时钟和 16 位累加器的设计。该设计是参数化的,因此很容易定制。

parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
parameter BaudGeneratorAccWidth = 16;
parameter BaudGeneratorInc = (Baud<<BaudGeneratorAccWidth)/ClkFrequency;reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc;
always @(posedge clk)BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc;wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth]; 

最后一个实现问题:"BaudGeneratorInc "的计算是错误的,这是因为 Verilog 使用 32 位中间结果,而计算结果超过了 32 位。为解决这个问题,请修改如下内容。

parameter BaudGeneratorInc = ((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4); 

这条线的另一个优点是将结果四舍五入,而不是截断。

现在我们有了足够精确的波特发生器,就可以继续使用 RS-232 发送器和接收器模块了。

  1. 代码实现
module BaudTickGen(input  wire clk, enable,output wire tick  // generate a tick at the specified baud rate * oversampling
);
parameter ClkFrequency = 25000000;
parameter Baud = 115200;
parameter Oversampling = 1;function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction
localparam AccWidth = log2(ClkFrequency/Baud)+8;  // +/- 2% max timing error over a byte
reg [AccWidth:0] Acc = 0;
localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth));  // this makes sure Inc calculation doesn't overflow
localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter))+(ClkFrequency>>(ShiftLimiter+1)))/(ClkFrequency>>ShiftLimiter);
always @(posedge clk) if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0]; else Acc <= Inc[AccWidth:0];
assign tick = Acc[AccWidth];
  1. 解释
    这段Verilog代码看起来是在实现一个串口通信的波特率时钟生成器。我们逐段解释一下代码:
  • 模块定义:

    module BaudTickGen(input  wire clk, enable,output wire tick  // 根据指定的波特率 * 过采样率生成时钟脉冲
    );
    

    这个模块有三个端口:clkenable 作为输入,tick 作为输出。它旨在根据指定的波特率和过采样率生成一个时钟脉冲信号。

  • 参数声明:

    parameter ClkFrequency = 25000000;
    parameter Baud = 115200;
    parameter Oversampling = 1;
    

    这些参数定义了时钟频率 (ClkFrequency)、所需波特率 (Baud) 以及过采样因子 (Oversampling)。您可以根据需要自定义这些值。

  • Log2 函数:

    function integer log2(input integer v);beginlog2 = 0;while (v >> log2)log2 = log2 + 1;end
    endfunction
    

    这是一个简单的函数,用于计算输入整数 v 的以2为底的对数。

  • 局部参数:

    localparam AccWidth = log2(ClkFrequency/Baud) + 8;
    

    AccWidth 是根据所需的位数来表示累加值 (Acc),以实现在一个字节内最大的时序误差不超过 +/- 2%。

    localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth));
    

    ShiftLimiter 用于限制左移操作在计算 Inc 时以防止溢出。

    localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter)) + (ClkFrequency>>(ShiftLimiter+1))) / (ClkFrequency>>ShiftLimiter);
    

    Inc 是每个时钟周期添加到累加器的增量值,以实现所需的波特率。

    always @(posedge clk) if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0];else Acc <= Inc[AccWidth:0];
    

    这个总是块在时钟的上升沿触发 (posedge clk)。如果 enable 为真,则将 Inc 的值累加到累加器 Acc 中;否则,将 Acc 设置为 Inc 的初始值。

  • 赋值语句:

    assign tick = Acc[AccWidth];
    

    这将累加器的最高位赋给 tick 输出,这就是生成的时钟脉冲信号。

3.RS-232 transmitter

我们正在构建一个参数固定的 “异步发射机”:8 个数据位、2 个停止位、无奇偶校验。
在这里插入图片描述
工作原理是这样的:

发送器在 FPGA 内获取 8 位数据并将其序列化(从 "TxD_start "信号断定时开始)。
在传输过程中,"忙 "信号被断开(在此期间 "TxD_start "信号被忽略)。
数据序列化

要完成起始位、8 个数据位和停止位,似乎应该使用状态机。

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; // start4'b1000: if(BaudTick) state <= 4'b1001; // bit 04'b1001: if(BaudTick) state <= 4'b1010; // bit 14'b1010: if(BaudTick) state <= 4'b1011; // bit 24'b1011: if(BaudTick) state <= 4'b1100; // bit 34'b1100: if(BaudTick) state <= 4'b1101; // bit 44'b1101: if(BaudTick) state <= 4'b1110; // bit 54'b1110: if(BaudTick) state <= 4'b1111; // bit 64'b1111: if(BaudTick) state <= 4'b0001; // bit 74'b0001: if(BaudTick) state <= 4'b0010; // stop14'b0010: if(BaudTick) state <= 4'b0000; // stop2default: if(BaudTick) state <= 4'b0000;
endcase

现在,我们只需生成 "TxD "输出。

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); 
完整代码:
module async_transmitter(input wire clk,input wire TxD_start,input wire [7:0] TxD_data,output wire TxD,output wire TxD_busy
);// Assert TxD_start for (at least) one clock cycle to start transmission of TxD_data
// TxD_data is latched so that it doesn't have to stay valid while it is being sentparameter ClkFrequency = 25000000;	// 25MHz
parameter Baud = 115200;// generate
// 	if(ClkFrequency<Baud*8 && (ClkFrequency % Baud!=0)) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Frequency incompatible with requested Baud rate");
// endgenerate`ifdef SIMULATION
wire BitTick = 1'b1;  // output one bit per clock cycle
`else
wire BitTick;
BaudTickGen #(ClkFrequency, Baud) tickgen(.clk(clk), .enable(TxD_busy), .tick(BitTick));
`endifreg [3:0] TxD_state = 0;
wire TxD_ready = (TxD_state==0);
assign TxD_busy = ~TxD_ready;reg [7:0] TxD_shift = 0;
always @(posedge clk)
beginif(TxD_ready & TxD_start)TxD_shift <= TxD_data;elseif(TxD_state[3] & BitTick)TxD_shift <= (TxD_shift >> 1);case(TxD_state)4'b0000: if(TxD_start) TxD_state <= 4'b0100;4'b0100: if(BitTick) TxD_state <= 4'b1000;  // start bit4'b1000: if(BitTick) TxD_state <= 4'b1001;  // bit 04'b1001: if(BitTick) TxD_state <= 4'b1010;  // bit 14'b1010: if(BitTick) TxD_state <= 4'b1011;  // bit 24'b1011: if(BitTick) TxD_state <= 4'b1100;  // bit 34'b1100: if(BitTick) TxD_state <= 4'b1101;  // bit 44'b1101: if(BitTick) TxD_state <= 4'b1110;  // bit 54'b1110: if(BitTick) TxD_state <= 4'b1111;  // bit 64'b1111: if(BitTick) TxD_state <= 4'b0010;  // bit 74'b0010: if(BitTick) TxD_state <= 4'b0000;  // stop1//4'b0011: if(BitTick) TxD_state <= 4'b0000;  // stop2default: if(BitTick) TxD_state <= 4'b0000;endcase
endassign TxD = (TxD_state<4) | (TxD_state[3] & TxD_shift[0]);  // put together the start, data and stop bits
endmodule
4.RS-232 receiver

我们正在构建一个 “异步接收器”:
在这里插入图片描述
我们的实施工作就是这样:

模块从 RxD 线路中收集数据。
当接收到一个字节时,它就会出现在 "数据 "总线上。一旦接收到一个完整的字节,"data_ready "就会断言一个时钟。

请注意,"数据 "只有在 "数据就绪 "断言时才有效。其余时间不要使用它,因为可能会有新的数据出现,从而对它进行洗牌。

Oversampling

异步接收器必须以某种方式与接收到的信号同步(它通常无法访问发送器使用的时钟)。

为了确定新数据字节何时到来,我们以波特率频率的倍数对信号进行过采样,寻找 "起始 "位。
一旦检测到 "起始 "位,我们就以已知的波特率对线路进行采样,以获取数据位。

接收器通常以 16 倍波特率对输入信号进行过采样。这里我们使用 8 倍… 对于 115200 波特,采样率为 921600Hz。

假设我们有一个 "Baud8Tick "信号,每秒发出 921600 次。

The design

首先,输入的 "RxD "信号与我们的时钟无关。我们使用两个 D 触发器对其进行过采样,并使其与我们的时钟域同步。

reg [1:0] RxD_sync;
always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0], RxD}; 

我们对数据进行过滤,以免将 RxD 线路上的短尖峰误认为是起始位。

reg [1:0] RxD_cnt;
reg RxD_bit;always @(posedge clk)
if(Baud8Tick)
beginif(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1;elseif(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1;if(RxD_cnt==2'b00) RxD_bit <= 0;elseif(RxD_cnt==2'b11) RxD_bit <= 1;
end

一旦检测到 “start”,状态机就会对接收到的每个比特进行处理。

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 04'b1001: if(next_bit) state <= 4'b1010; // bit 14'b1010: if(next_bit) state <= 4'b1011; // bit 24'b1011: if(next_bit) state <= 4'b1100; // bit 34'b1100: if(next_bit) state <= 4'b1101; // bit 44'b1101: if(next_bit) state <= 4'b1110; // bit 54'b1110: if(next_bit) state <= 4'b1111; // bit 64'b1111: if(next_bit) state <= 4'b0001; // bit 74'b0001: if(next_bit) state <= 4'b0000; // stop bitdefault: state <= 4'b0000;
endcase

请注意,我们使用了一个 "next_bit "信号,以便从一个比特到另一个比特。

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); 

最后,移位寄存器将数据位收集起来。

reg [7:0] RxD_data;
always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]}; 
完整代码
module async_receiver(input wire clk,input wire RxD,output reg RxD_data_ready,input wire RxD_clear,output reg [7:0] RxD_data  // data received, valid only (for one clock cycle) when RxD_data_ready is asserted
);parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;parameter Oversampling = 8;  // needs to be a power of 2
// we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time
// 8 times oversampling by default, use 16 for higher quality reception// generate
// 	if(ClkFrequency<Baud*Oversampling) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Frequency too low for current Baud rate and oversampling");
// 	if(Oversampling<8 || ((Oversampling & (Oversampling-1))!=0)) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Invalid oversampling value");
// endgenerate// We also detect if a gap occurs in the received stream of characters
// That can be useful if multiple characters are sent in burst
//  so that multiple characters can be treated as a "packet"
wire RxD_idle;  // asserted when no data has been received for a while
reg RxD_endofpacket; // asserted for one clock cycle when a packet has been detected (i.e. RxD_idle is going high)reg [3:0] RxD_state = 0;`ifdef SIMULATION
wire RxD_bit = RxD;
wire sampleNow = 1'b1;  // receive one bit per clock cycle`else
wire OversamplingTick;
BaudTickGen #(ClkFrequency, Baud, Oversampling) tickgen(.clk(clk), .enable(1'b1), .tick(OversamplingTick));// synchronize RxD to our clk domain
reg [1:0] RxD_sync = 2'b11;
always @(posedge clk) if(OversamplingTick) RxD_sync <= {RxD_sync[0], RxD};// and filter it
reg [1:0] Filter_cnt = 2'b11;
reg RxD_bit = 1'b1;always @(posedge clk)
if(OversamplingTick)
beginif(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11) Filter_cnt <= Filter_cnt + 1'd1;else if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00) Filter_cnt <= Filter_cnt - 1'd1;if(Filter_cnt==2'b11) RxD_bit <= 1'b1;elseif(Filter_cnt==2'b00) RxD_bit <= 1'b0;
end// and decide when is the good time to sample the RxD line
function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction
localparam l2o = log2(Oversampling);
reg [l2o-2:0] OversamplingCnt = 0;
always @(posedge clk) if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1;
wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1);
`endif// now we can accumulate the RxD bits in a shift-register
always @(posedge clk)
case(RxD_state)4'b0000: if(~RxD_bit) RxD_state <= `ifdef SIMULATION 4'b1000 `else 4'b0001 `endif;  // start bit found?4'b0001: if(sampleNow) RxD_state <= 4'b1000;  // sync start bit to sampleNow4'b1000: if(sampleNow) RxD_state <= 4'b1001;  // bit 04'b1001: if(sampleNow) RxD_state <= 4'b1010;  // bit 14'b1010: if(sampleNow) RxD_state <= 4'b1011;  // bit 24'b1011: if(sampleNow) RxD_state <= 4'b1100;  // bit 34'b1100: if(sampleNow) RxD_state <= 4'b1101;  // bit 44'b1101: if(sampleNow) RxD_state <= 4'b1110;  // bit 54'b1110: if(sampleNow) RxD_state <= 4'b1111;  // bit 64'b1111: if(sampleNow) RxD_state <= 4'b0010;  // bit 74'b0010: if(sampleNow) RxD_state <= 4'b0000;  // stop bitdefault: RxD_state <= 4'b0000;
endcasealways @(posedge clk)
if(sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};//reg RxD_data_error = 0;
always @(posedge clk)
beginif(RxD_clear)RxD_data_ready <= 0;elseRxD_data_ready <= RxD_data_ready | (sampleNow && RxD_state==4'b0010 && RxD_bit);  // make sure a stop bit is received//RxD_data_error <= (sampleNow && RxD_state==4'b0010 && ~RxD_bit);  // error if a stop bit is not received
end`ifdef SIMULATION
assign RxD_idle = 0;
`else
reg [l2o+1:0] GapCnt = 0;
always @(posedge clk) if (RxD_state!=0) GapCnt<=0; else if(OversamplingTick & ~GapCnt[log2(Oversampling)+1]) GapCnt <= GapCnt + 1'h1;
assign RxD_idle = GapCnt[l2o+1];
always @(posedge clk) RxD_endofpacket <= OversamplingTick & ~GapCnt[l2o+1] & &GapCnt[l2o:0];
`endifendmodule
5.How to use the RS-232 transmitter and receiver

该设计允许通过 PC 控制几个 FPGA 引脚(通过 PC 的串行端口)。

它在 FPGA 上创建了 8 个输出(端口名为 "GPout")。FPGA 接收到的任何字符都会更新 GPout。
同时在 FPGA 上创建 8 个输入端(名为 "GPin "的端口)。每当 FPGA 接收到一个字符,GPin 就会传输一次。

GP 输出可用于从电脑远程控制任何东西,可能是 LED 或咖啡机…

module serialGPIO(input clk,input RxD,output TxD,output reg [7:0] GPout,  // general purpose outputsinput [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

龙芯杯个人赛串口信号连接

// | 0xBFD003F8 | [7:0] | 串口数据,读、写地址分别表示串口接收、发送一个字节 |
// | 0xBFD003FC | [0]   | 只读,为1时表示串口空闲,可发送数据                |
// | 0xBFD003FC | [1]   | 只读,为1时表示串口收到数据                        |
module uart(input wire clk,input wire resetn,// read and write from cpu// input   wire          conf_en,     input   wire          conf_re,conf_we,// input   wire   [3 :0] conf_wen,      input   wire   [31:0] conf_addr,    input   wire   [31:0] conf_wdata,   output  wire   [31:0] conf_rdata,// read and write to device on board//直连串口信号output wire txd,  //直连串口发送端input  wire rxd  //直连串口接收端// output reg [15:0] led,// input wire [7:0] switch
);
wire read_flag;
wire write_uart;
wire read_uart;
assign read_flag = ((conf_addr == 32'hbfd003fc) && (conf_re)) ? 1'b1:1'b0;
assign read_uart = ((conf_addr == 32'hbfd003f8) && (conf_re)) ? 1'b1:1'b0;
assign write_uart = ((conf_addr == 32'hbfd003f8) && (conf_we)) ? 1'b1:1'b0;wire [7:0] ext_uart_wdata;// write data// buffers
reg [7:0] ext_uart_rbuffer;// read buffer
reg [1:0] ext_uart_flag; // uart flag for read and write at addr 0xbfd003fc
assign conf_rdata = read_flag?{30'h0,ext_uart_flag}:read_uart?{24'h0,ext_uart_rbuffer}:32'h0;wire [7:0] ext_uart_rx;
wire ext_uart_ready,ext_uart_clear;wire ext_uart_busy; // transmitter busy flag
reg ext_uart_start; // transmitter start work signal
always @(posedge clk) beginif(!resetn)ext_uart_flag <= 2'h1;else beginif(ext_uart_ready)ext_uart_flag[1] <= 1;else if(read_uart)  ext_uart_flag[1] <= 0;// write flag// if(write_uart)//     ext_uart_flag[0] <= 0;// else if(!ext_uart_busy)//     ext_uart_flag[0] <= 1;if (!ext_uart_busy)ext_uart_flag[0] <= 1;else if(write_uart)ext_uart_flag[0] <= 0;endend//uart reciever
async_receiver #(.ClkFrequency(60000000),.Baud(9600)) ext_uart_r(.clk(clk),.RxD(rxd),.RxD_data_ready(ext_uart_ready),.RxD_clear(ext_uart_clear),.RxD_data(ext_uart_rx));
// store RxD_data to read buffer and clear RxD_data after store
assign ext_uart_clear = ext_uart_ready;
// assign ext_uart_rdata = ext_uart_rx;
always @(posedge clk) beginif(ext_uart_ready)ext_uart_rbuffer <= ext_uart_rx;
end
// assign ext_uart_rbuffer = ext_uart_rx;always @(posedge clk) beginif(!ext_uart_busy && write_uart)ext_uart_start <= 1'b1;else ext_uart_start <= 1'b0;
endwire [7:0] ext_uart_tx;// write data
reg [7:0] last_data;
always @(posedge clk) beginlast_data <= ext_uart_tx[7:0];
end
assign ext_uart_tx = write_uart?conf_wdata[7:0]:last_data;
// assign ext_uart_tx = write_uart?conf_wdata[7:0]:8'h0;async_transmitter #(.ClkFrequency(6000_0000),.Baud(9600)) ext_uart_t(.clk(clk),.TxD(txd),.TxD_busy(ext_uart_busy),.TxD_start(ext_uart_start),.TxD_data(ext_uart_tx) // transmit the data in buffer to txd);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/225373.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux之vim编辑器

目录 vim编辑器 vim编辑器指令 命令模式指令 光标相关 移动光标相关 文本操作 底行模式指令 插入模式 vim配置 vimforcpp 面试官&#xff1a;小伙子&#xff0c;你是用什么环境编写代码的&#xff1f; 小明&#xff1a;vs2019 面试官&#xff1a;小伙子&#xff0c…

基于AT89C51单片机的8位密码锁仿真与实物制作

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/88657969?spm1001.2014.3001.5503 源码获取 C 源码仿真图毕业设计实物制作步骤01 摘要 在日常的生活和工作中, 住宅与部门的安全防范、单位的文件档案、财务报表…

web前端html笔记2

新增状态标签<meter><progress> <meter> 属性 值 描述 high 数值 规定高值 low 数值 规定低值 max 数值 规定最大值 min 数值 规定最小值 optimum 数值 规定最优值 value 数值 规定当前值 <body> <meter high"50" …

【Qt之Quick模块】5. QML基本类型及示例用法

QML格式 以下是一个QML文件 import QtQuick 2.12Window {id: mainWindowwidth: 400height: 300visible: truetitle: "My QML Application"Rectangle {id: rectwidth: 200height: 100color: "red"Text {id: textItemtext: "Hello World!"font.p…

ueditor富文本编辑器中图片上传地址配置以及抓取远程图片地址的配置

一&#xff1a;图片上传保存地址配置 打开文件ueditor.php,找到imagePathFormat进行修改即可 一&#xff1a;远程抓取图片配置 打开文件ueditor.config.js,找到catchRemoteImageEnable&#xff0c;取消注释即可

python dash 写一个登陆页 4

界面 代码&#xff1a; 这里引入了dash_bootstrap_components 进行界面美化 &#xff0c;要记一些className&#xff0c;也不是原来说的不用写CSS了。 from dash import Dash, html, dcc, callback, Output, Input, State import dash_bootstrap_components as dbc app Dash…

[Linux] MySQL数据表(数据结构)管理

一、数据库 1.1 数据库的基本概念 数据库&#xff08;database&#xff09;是用来组织、存储和管理数据的仓库 数据库管理系统&#xff08;DBMS&#xff09;&#xff1a;是实现对数据有效组织&#xff0c;管理和存取的系统软件。 数据的建立和维护功能&#xff0c;数据定义…

STM32实战之深入理解I²C通信协议

目录 IC的物理层 IC的协议层 IC特点 IC 总线时序图 软件模拟IC时序分享 例程简介 例程分享 STM32的IC外设 IIC&#xff08;Inter-Integrated Circuit&#xff09;&#xff0c;也称为IC或TWI&#xff08;Two-Wire Interface&#xff09;&#xff0c;是一种广泛使用的串行…

Apache Flink 进阶教程(六):Flink 作业执行深度解析

目录 前言 Flink 四层转化流程 Program 到 StreamGraph 的转化 StreamGraph 到 JobGraph 的转化 为什么要为每个 operator 生成 hash 值&#xff1f; 每个 operator 是怎样生成 hash 值的&#xff1f; JobGraph 到 ExexcutionGraph 以及物理执行计划 Flink Job 执行流程…

什么是EMC工程师?

摘要: 今天来介绍一下什么是EMC工程师。一 EMC工程师起源要了解什么是EMC工程师&#xff0c;我们首先要了解什么是EMC。 今天来介绍一下什么是EMC工程师。 一 EMC工程师起源 要了解什么是EMC工程师&#xff0c;我们首先要了解什么是EMC。 工程师这个职业相信大家都耳熟能详…

Maven之插件入门

官方文档&#xff1a;https://maven.apache.org/guides/plugin/guide-java-plugin-development.html 命名规范 <yourplugin>-maven-plugin 创建项目 生成项目 方式一、IDEA 2023 方式二、命令行 mvn archetype:generate -DgroupIdcn.lsj -DartifactIdhello-maven-pl…

接口测试学习笔记

文章目录 认识urlhttp协议接口规范Postman实现接口测试设计接口测试用例使用软件发送请求并查看响应结果Postman 自动关联Postman如何提交multipart/form-data请求数据Postman如何提交查询参数Postman 如何批量执行用例单接口测试Postman 断言Postman参数化 接口测试自动化requ…

RabbitMQ入门指南(九):消费者可靠性

专栏导航 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、消费者确认机制 二、失败重试机制 三、失败处理策略 四、业务幂等性 1.通过唯一标识符保证操作的幂等性 2.通过业务判断保证操作的幂等性 总结 前言 RabbitMQ是一个高效、可靠的开源消息队列系…

TikTok地理标签:通过短视频游走全球景点

TikTok&#xff0c;这个全球短视频平台&#xff0c;不仅是创意的播放场所&#xff0c;更是连接全球用户的数字旅行通道。通过TikTok的地理标签&#xff0c;用户可以在短视频中游走于世界各地的景点&#xff0c;感受异国风情&#xff0c;分享旅行心情。本文将深入探讨TikTok地理…

7.3 uvm_config_db in UVM

uvm_config_db类派生自uvm_resource_db类。它是uvm_resource_db顶部的另一层便利层&#xff0c;简化了用于uvm_component实例的基本接口&#xff08;资源库的访问方法&#xff09;。 下面uvm_config_db类的代码段取自uvm源代码。 class uvm_config_db#(type Tint) extends uv…

【GitHub精选项目】短信系统测试工具:SMSBoom 操作指南

前言 本文为大家带来的是 OpenEthan 开发的 SMSBoom 项目 —— 一种用于短信服务测试的工具。这个工具能够发送大量短信&#xff0c;通常用于测试短信服务的稳定性和处理能力。在合法和道德的范畴内&#xff0c;SMSBoom 可以作为一种有效的测试工具&#xff0c;帮助开发者和系统…

【编译原理】基于词法分析器的LL1语法分析器

【编译原理】基于词法分析器的LL1语法分析器 实验要求 设计一个满足以下要求的⽂法&#xff1a; &#xff08;1&#xff09;识别只包含变量声明语句和执行语句程序段的语法结构合法性&#xff1b; &#xff08;2&#xff09;变量声明中只使用int,char,float 3类基本类型&…

Android studio 花式按键

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

【C++】STL 容器 - list 双向链表容器 ① ( 容器特点 | 容器操作时间复杂度 | 构造函数 )

文章目录 一、 list 双向链表容器简介1、容器特点2、容器操作时间复杂度3、遍历访问5、头文件 二、 list 双向链表容器 构造函数1、默认无参构造函数2、创建包含 n 个相同元素的 list 双向链表3、使用初始化列表构造 list 双向链表4、使用另外一个 list 容器 构造 list 双向链表…

【Java基础】Java中异常分类,他们之间的区别?

&#x1f341;Java中异常分哪两类 &#x1f341;Java中异常类&#x1f341;受检异常&#x1f341;非受检异常 &#x1f341;拓展知识仓&#x1f341;什么是Throwable&#x1f341;Error和Exception的区别和联系&#x1f341; 列举几个常用的RuntimeException&#x1f341;Java异…