03_Puerto serie RS232

1. Estudio teórico

1.1 Introducción a la terminología del puerto serie

1.1.1 Comunicación asíncrona

La diferencia entre UART, SPI e IIC es que es una interfaz de comunicación asíncrona. El receptor en comunicación asíncrona
no sabe cuándo llegarán los datos, por lo que ambas partes deben tener sus propios relojes. Durante el proceso de transmisión de datos, no hay necesidad de un reloj. El intervalo de tiempo para el envío por parte del remitente puede ser desigual, y el receptor realiza la sincronización de la información con la ayuda del bit de inicio y el bit de parada de los datos.

1.1.2 Comunicación síncrona

SPI e IIC son interfaces de comunicación síncrona (los detalles se presentarán en capítulos posteriores).
En la comunicación síncrona, ambas partes usan relojes con la misma frecuencia. Durante la transmisión de datos, el reloj se transmite junto con los datos. Los relojes utilizados por el remitente y el receptor son proporcionados por el host.

1.1.3 Dúplex completo

Full-duplex, es decir, se pueden enviar y recibir datos al mismo tiempo.

1.1.4 Semidúplex

Solo uno de envío de datos y recepción de datos puede estar en progreso al mismo tiempo.

1.1.5 Comunicación UART

Solo hay dos líneas de señal en la comunicación UART, una es la línea del puerto de datos de envío llamada tx, y la otra es la línea del puerto de datos de recepción llamada rx. Para la PC, su tx debe estar conectado al rx de la FPGA. De manera similar, el rx de la PC debe estar conectado al tx de la FPGA. Si se conectan dos tx o dos rx, los datos no se pueden enviar y recibir normalmente, así que no se confunda, recuerde que rx y tx son relativos al cuerpo principal. UART puede lograr dúplex completo, es decir, puede enviar y recibir datos al mismo tiempo.
inserte la descripción de la imagen aquí

1.2 Línea de señal RS-232

El estándar de puerto de línea de señal RS-232 se usa a menudo para la comunicación entre computadoras, enrutadores y módems (MODEN, comúnmente conocido como "
cat"). En este sistema de comunicación, el equipo se divide en equipo terminal de datos DTE (computadora, enrutador) y equipo de comunicación de datos DCE (módem). Usamos este modelo de comunicación para explicar su conexión de línea de señal y el rol de cada línea de señal. En las computadoras de escritorio antiguas, generalmente hay un puerto COM estándar RS-232 (también conocido como interfaz DB9).La
inserte la descripción de la imagen aquí
línea de señal de la interfaz DB9 indica
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
la conexión del puerto serie "directo".
inserte la descripción de la imagen aquí

1.3 Introducción al protocolo de comunicación RS232

1) RS232 es una especie de UART, no hay línea de reloj, solo dos líneas de datos, a saber, rx y tx, ambas de 1 bit de ancho. Donde rx es la línea para recibir datos y tx es la línea para enviar datos.
2) El ancho de bit rx es de 1 bit.Cuando la PC envía datos de 8 bits a la FPGA a través del asistente de depuración del puerto serie, la FPGA recibe bit a bit a través del puerto serie rx, desde el bit más bajo hasta el bit más alto, y finalmente los bits se empalman en datos de 8 bits en la FPGA. (Los datos recibidos finalmente se empalman en datos de 8 bits, datos de un bit)
3) El ancho del bit tx es de 1 bit. Cuando la FPGA envía datos de 8 bits a la PC a través del puerto serial, la FPGA transmite los datos de 8 bits a la PC uno por uno a través de la línea tx y los envía secuencialmente desde el bit más bajo hasta el bit más alto. Finalmente, la computadora host empalma los bits de datos bit por bit en datos de 8 bits a través de el asistente de puerto serie según el protocolo RS232.
4) El envío y recepción de datos del puerto serie se basa en la estructura de la trama, es decir, el envío y la recepción de datos trama por trama. Además de contener 8 bits de datos válidos en el medio, cada trama debe tener un bit de inicio al principio de cada trama, que se fija en 0; al final de cada trama, también debe haber un bit de parada, que se fija en 1, es decir, la estructura de trama más básica (excluyendo la paridad, etc.) tiene 10 bits . En el caso de no enviar o recibir datos, rx y tx están en estado inactivo . En este momento, las líneas rx y tx se mantienen en
un nivel alto. Si hay una transmisión de trama de datos, primero habrá un bit de inicio, luego bits de datos de 8 bits y luego un bit de parada de 1 bit. Luego, rx y tx continúan ingresando al estado inactivo y luego esperan la siguiente transmisión de datos. Como se muestra en la figura, hay una estructura de trama RS232 básica.
inserte la descripción de la imagen aquí

5) Tasa de baudios: en el canal de transmisión de información, la unidad de señal que transporta información de datos se denomina símbolo (porque el puerto serie es de 1 bit para la transmisión, por lo que el símbolo representa un número binario), el número de símbolos transmitidos a través de la señal por segundo se denomina tasa de transmisión del símbolo, denominada tasa de baudios, símbolo de uso común "baudios", y su unidad es "baudios por segundo (Bps)". Las tasas de baudios comunes de los puertos seriales son 4800, 9600, 115200, etc. Elegimos una tasa de baudios de 9600 para explicar el capítulo del puerto serial.

6) Tasa de bits: La cantidad de información transmitida por el canal de comunicación por segundo se denomina tasa de transmisión de bits, denominada tasa de bits, y su unidad es "bits por segundo (bps)". La tasa de bits se puede calcular a partir de la tasa de baudios, la fórmula es: tasa de bits = tasa de baudios * dígitos binarios correspondientes a un solo estado de modulación. Si la tasa de baudios es 9600, la tasa de bits del puerto serie es: 9600Bps 1 bit = 9600bps.

7) La diferencia entre la tasa de baudios y la tasa de bits
Para analizar la relación entre la tasa de bits y la tasa de baudios, es necesario comprender la relación entre el elemento de decodificación y el bit. Al igual que los autobuses, el metro y los taxis mencionados en el ejemplo anterior pueden llevar diferentes números de pasajeros, los diferentes elementos del código también se pueden representar con bits de diferentes dígitos. El número de bits necesarios para un símbolo está determinado por el número de estados admitidos por el símbolo.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
8) Cálculo: si la tasa de baudios es 9600, el tiempo calculado para que el puerto serial envíe o reciba datos de 1 bit es un baudio, es decir, 1/9600 segundos.Si se usa un reloj de sistema de 50 MHz (período de 20 ns) para contar, el número a contar es cnt = (1 s * 10 ^ 9) ns / 9600 bits) ns / 20 ns ≈ 5208 sistema ciclos de reloj, es decir, el intervalo entre cada bit de datos debe estar a una frecuencia de reloj de 50 MHz Cuenta regresiva 5208 veces.
9) Cuando la computadora host envía datos de 8 bits a través del puerto serie, enviará automáticamente un bit de inicio de tiempo en baudios antes de enviar datos válidos de 8 bits, y enviará automáticamente un bit de parada después de enviar datos válidos de 8 bits. De la misma manera, antes de que el asistente del puerto serie reciba los datos enviados por la computadora host, debe detectar el bit de inicio de un tiempo de baudios para comenzar a recibir datos.Después de recibir los datos de 8 bits, recibirá un bit de parada del tiempo de baudios.

2. Estado metaestable

2.1 Tome el primer tiempo

inserte la descripción de la imagen aquí
1) ¿Por qué tomar el primer latido, porque el rx entrante y la señal del reloj no están sincronizados, y la señal rx debe sincronizarse con la señal del reloj?
2) Dado que la velocidad en baudios y la señal rx en la PC son síncronas, y la señal rx y el reloj del sistema sys_clk de la FPGA son asíncronos, lo que debemos hacer en este momento es sincronizar la señal rx en el sistema de dominio de reloj lento (la velocidad en baudios en la PC) con el sistema de dominio de reloj rápido (sys_clk en la FPGA).

2.2 Latido continuo

1) ¿Qué es el estado metaestable?
Porque al transmitir datos en el puerto serial, cuando usa un osciloscopio para amplificar el borde ascendente o descendente de un pulso rectangular, encontrará que sus bordes ascendentes y descendentes no suben o bajan instantáneamente, sino que tienen un proceso de cambio de pendiente, que se denomina "velocidad de respuesta" en el amplificador operacional. En este momento, la terminal de salida del registro de primer nivel de la FPGA está en un estado incierto durante un largo período de tiempo después de que llega el borde del reloj, y está en un estado oscilante entre 0 y 1, en lugar de igual al valor rx determinado de la entrada del puerto serie.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí2) ¿Cómo se genera el estado metaestable?
El tiempo de configuración Tus, el tiempo de espera Th, el tiempo de configuración de RS y el tiempo de guardado no cumplen las condiciones, el retraso de registro Tco, el tiempo de decisión Tmet y el registro de entrada de señal no garantizan su tiempo de configuración y tiempo de espera.
inserte la descripción de la imagen aquí

3) ¿Cómo resolver el estado metaestable?
Utilice registros de varios niveles para reducir el daño de la metaestabilidad en el sistema. Generalmente golpea
2 tiempos.
inserte la descripción de la imagen aquí

3. Objetivos experimentales

Diseñe y realice el módulo de recepción y envío de datos basado en el puerto serial RS232, use el módulo de recepción y envío para completar el experimento de loopback de datos del puerto serial.
inserte la descripción de la imagen aquí

4. Recursos de hardware

MAX3232 es un chip transceptor RS232. Dado que el controlador no puede reconocer directamente las señales del estándar de nivel RS-232, estas señales se convertirán en señales de nivel "TTL" que el controlador puede reconocer a través de un "chip de conversión de nivel" para realizar la comunicación.
inserte la descripción de la imagen aquí

5. Diseño modular

5.1 Diagrama de bloques del módulo de nivel superior

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

5.2 Módulo de recepción de datos del puerto serie

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
¿Por qué es necesario emitir una señal de bandera válida que acompañe a los datos paralelos? Esto se debe a que el módulo o sistema subsiguiente puede no saber si los datos muestreados en ese momento son estables y válidos cuando se usan los datos paralelos, y la llegada de la señal de bandera válida de datos indica que los datos son estables y válidos en ese momento, lo que sirve como indicador. Cuando la señal del indicador de datos válidos es alta, los datos paralelos pueden ser utilizados por el módulo o sistema subsiguiente.
Enviar 87654321 —> 87654321
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

5.3 Módulo de envío de datos en serie

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

6. Diagrama de forma de onda

6.1 Módulo de recepción de datos en serie

start_nedge: flanco descendente, señal de bandera de inicio, a veces la señal rex_reg también tendrá un salto 0, 1, y habrá un retraso descendente en el bit de datos Para evitar esta situación, configure una señal de habilitación work_en, y la señal work_en puede juzgar que la señal de bandera start_nedge que aparece en este momento no es el flanco descendente inicial de la trama del puerto serie que queremos, para que pueda filtrarse.
inserte la descripción de la imagen aquí
baud_cnt: distingue entre datos de 10 bits y de 10 bits
bit_flag en un marco de datos para extraer datos y encontrar el estado más estable.
inserte la descripción de la imagen aquí
Extraiga datos de 8 bits, datos válidos 1-8,
inserte la descripción de la imagen aquí
después de que finalice la señal efectiva, work_en se pone bajo, el contador también se borra a 0,
inserte la descripción de la imagen aquí
y luego se realiza la operación de empalme de datos, y finalmente se emiten los datos y la señal po_flag se pone alta.
inserte la descripción de la imagen aquí

6.2 Módulo de envío de datos en serie

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

7. RTL

7.1 uart_rx

`timescale  1ns/1ns




module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {
    
    rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

7.2 uart_tx

`timescale  1ns/1ns




module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号
 
     output  reg             tx              //串转并后的1bit数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx <= 1'b1; //空闲状态时为高电平
        else    if(bit_flag == 1'b1)
            case(bit_cnt)
                0       : tx <= 1'b0;
                1       : tx <= pi_data[0];
                2       : tx <= pi_data[1];
                3       : tx <= pi_data[2];
                4       : tx <= pi_data[3];
                5       : tx <= pi_data[4];
                6       : tx <= pi_data[5];
                7       : tx <= pi_data[6];
                8       : tx <= pi_data[7];
                9       : tx <= 1'b1;
                default : tx <= 1'b1;
            endcase

endmodule

7.3 rs232

`timescale  1ns/1ns




module  rs232
(
    input   wire    sys_clk     ,   //系统时钟50MHz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    rx          ,   //串口接收数据

    output  wire    tx              //串口发送数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ uart_rx_inst ------------------------
uart_rx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_rx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .rx         (rx         ),  //input             rx
            
    .po_data    (po_data    ),  //output    [7:0]   po_data
    .po_flag    (po_flag    )   //output            po_flag
);

//------------------------ uart_tx_inst ------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .pi_data    (po_data    ),  //input     [7:0]   pi_data
    .pi_flag    (po_flag    ),  //input             pi_flag
                
    .tx         (tx         )   //output            tx
);

endmodule

8. Banco de pruebas

8.1 tb_uart_rx

`timescale  1ns/1ns


module  tb_uart_rx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg             sys_clk;
reg             sys_rst_n;
reg             rx;

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'd0);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//定义一个名为rx_bit的任务,每次发送的数据有10位
//data的值分别为0~7由j的值传递进来
//任务以task开头,后面紧跟着的是任务名,调用时使用
task rx_bit(
    //传递到任务中的参数,调用任务的时候从外部传进来一个8位的值
        input   [7:0]   data
);
        integer i;      //定义一个常量
//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1
//不可以写成C语言i=i++的形式
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_rx uart_rx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .rx         (rx         ),  //input           rx
                
        .po_data    (po_data    ),  //output  [7:0]   po_data
        .po_flag    (po_flag    )   //output          po_flag
);

endmodule


8.2 tb_uart_tx

`timescale  1ns/1ns
/



module  tb_uart_tx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg [7:0]   pi_data;
reg         pi_flag;

//wire  define
wire        tx;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送7次数据,分别为0~7
initial begin
        pi_data <= 8'b0;
        pi_flag <= 1'b0;
        #200
        //发送数据0
        pi_data <= 8'd0;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
        #(5208*20*10);
        //发送数据1
        pi_data <= 8'd1;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据2
        pi_data <= 8'd2;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据3
        pi_data <= 8'd3;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据4
        pi_data <= 8'd4;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据5
        pi_data <= 8'd5;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据6
        pi_data <= 8'd6;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据7
        pi_data <= 8'd7;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_tx uart_tx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .pi_data    (pi_data    ),  //output  [7:0]   pi_data
        .pi_flag    (pi_flag    ),  //output          pi_flag

        .tx         (tx         )   //input           tx
);

endmodule

8.3 TB_rs232

`timescale  1ns/1ns




module  tb_rs232();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire  define
wire    tx          ;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    rx        <= 1'b1;
    #20;
    sys_rst_n <= 1'b1;
end

//调用任务rx_byte
initial begin
    #200
    rx_byte();
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task    rx_byte();  //因为不需要外部传递参数,所以括号中没有输入
    integer j;
    for(j=0; j<8; j=j+1)    //调用8次rx_bit任务,每次发送的值从0变化7
        rx_bit(j);
endtask

//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task    rx_bit(
    input   [7:0]   data
);
    integer i;
    for(i=0; i<10; i=i+1)   begin
        case(i)
            0: rx <= 1'b0;
            1: rx <= data[0];
            2: rx <= data[1];
            3: rx <= data[2];
            4: rx <= data[3];
            5: rx <= data[4];
            6: rx <= data[5];
            7: rx <= data[6];
            8: rx <= data[7];
            9: rx <= 1'b1;
        endcase
        #(5208*20); //每发送1位数据延时5208个时钟周期
    end
endtask

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ rs232_inst ------------------------
rs232   rs232_inst
(
    .sys_clk    (sys_clk    ),  //input         sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input         sys_rst_n
    .rx         (rx         ),  //input         rx

    .tx         (tx         )   //output        tx
);

endmodule



Supongo que te gusta

Origin blog.csdn.net/HeElLose/article/details/131404467
Recomendado
Clasificación