• 吃透Chisel语言.37.Chisel实战之以FIFO为例(二)——基于FIFO的串口通信:串口发送“Hello World!”


    Chisel实战之以FIFO为例(二)——基于FIFO的串口通信:串口发送“Hello World!”

    上一篇文章介绍了FIFO Buffer的概念,然后用Chisel实现了单Buffer的FIFO,接着又用单Buffer实现了完整的FIFO Buffer,即气泡Buffer。这种方法很简单,在数据率低于时钟频率的时候很好用,比如在作为串口的解耦合缓冲区的时候,这一篇文章就会介绍如何基于FIFO实现串口并实现串口发送“Hello World!”。

    串口的概念

    串行接口即串口(也叫UART,常用的是RS-232协议),是在个人笔记本电脑和FPGA板卡之间通信的最简单的方法。既然是串行接口,数据当然是串行传输的。比如对于一个8位的数据,从最低有效位bit(0)开始,然后每次传输一个或两个比特。如果没有数据要传输,那么输出就是1。下图展示了用串口传输一个字节数据的时序:

    在这里插入图片描述

    现在我们用模块化的设计我们的UART,每个模块只具备最小的功能。下面分别对发送端(TX),接收端(RX)和缓冲区进行设计,然后展示这些基本组件的用法。

    端口设计

    首先,我们需要一个接口,即端口的定义。对于UART设计,一般都会使用ready-valid握手接口,方向是从串口的发送端侧的角度来看的:

    class Channel extends Bundle {
        val data = Input(Bits(8.W))
        val ready = Output(Bool())
        val valid = Input(Bool())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用ready-valid接口的约定是当readyvalid接口都被设置为有效的时候数据才会传输。

    发送端Tx设计

    下面是基本的发送端的实现。

    class Tx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val txd = Output(Bits(1.W))
            val channel = new Channel()
        })
    
        val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).asUInt
    
        val shiftReg = RegInit(0x7ff.U) // 共11位,一个开始位,两个结束位
        val cntReg = RegInit(0.U(20.W))
        val bitsReg = RegInit(0.U(4.W))
    
        io.channel.ready := (cntReg === 0.U) && (bitsReg === 0.U)
        io.txd := shiftReg(0)
    
        when(cntReg === 0.U) {
            cntReg := BIT_CNT
            when(bitsReg =/= 0.U) {
                val shift = shiftReg >> 1
                shiftReg := Cat(1.U, shift(9, 0))
                bitsReg := bitsReg - 1.U
            }.otherwise {
                when(io.channel.valid) {
                    // 一个起始位0和两个结束位11
                    shiftReg := Cat(Cat(3.U, io.channel.data), 0.U)
                    bitsReg := 11.U
                }.otherwise {
                    shiftReg := 0x7ff.U
                }
                
            }
        }.otherwise {
            cntReg := cntReg - 1.U
        }
    }
    
    • 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

    它的IO端接口有:

    1. txd端口,用串行的方式发送数据;
    2. 一个Channel类型的端口channel,发送端可以接受字符进行串行化然后发送;

    为了生成正确的时序,我们通过计算一个串行位的时钟周期时间来计算常数BIT_CNT,过程的原理是这样的:

    1. 波特率baudRate就是串口每秒传输的二进制位数,单位为bps(bits per second),频率frequency就是时钟频率,那么frequency/baudRate就是每传输一个比特需要的时钟周期数;
    2. 但是frequency/baudRate可能是个小数,对于时钟周期数而言必须得是整数,而/运算符两边都是整数,结果就是个整数,而且是向下取整的整数,而我们希望结果是四舍五入得到的,因此需要先加上一个baudRate / 2再整除以baudRate
    3. 由于BIT_CNT是用于生成波特率的倒数计数器,因此应该从波特率-1开始,倒数到0

    设计中用到了三个寄存器:

    1. shiftReg:用于移位数据的寄存器(即序列化);
    2. cntReg:用于生成正确波特率的倒数计数器;
    3. bitsReg:用于存放还需要被移出的比特位的数量;

    没有其他的FSM状态需要额外编码,所有的状态都在这三个寄存器里面了。

    计数器cntReg是在持续运行的,倒数到0.U然后到0.U时重置到初始值。所有的动作都是在cntReg值为0.U的时候进行的。因为我们要构建最小的发送端,因此我们就用个移位寄存器来存储数据。因此,channel仅在cntReg0.U且没有需要移出的比特时才设置ready

    IO端口txd直接连接到移位寄存器的最低有效位上。

    bitsReg =/= 0.U时,即还有未移出的比特,我们右移寄存器并在开头补一个1.U(发送器的空闲电平)。如果没有比特需要移出了,那就检查channel是否包含数据(由valid端口给出),如果有的话,那就构造移位寄存器的值,右边是个前导起始位(0.U),中间是数据,左边是两位停止位(3.U11),共11位,因此bitsReg初始值为11.U

    这个很迷你的发送端实现没有额外的缓冲区,而且仅在移位寄存器为空且cntReg0.U的时候才能接受一个字符。仅在cntReg0.U时接受新数据意味着,ready标志也会在移位寄存器中有空间的时候被设置为无效。不过我们不希望把这种复杂性引入发送端,而是想把这件事委托给缓冲区来干。

    单字节缓冲区的实现

    首先我们需要实现一个单字节缓冲区,这个和Bubble FIFO中的FIFO寄存器是类似的。具体实现如下:

    class Buffer extends Module {
        val io = IO(new Bundle {
            val in = new Channel()
            val out = Flipped(new Channel())
        })
    
        val empty :: full :: Nil = Enum(2)
        val stateReg = RegInit(empty)
        val dataReg = RegInit(0.U(8.W))
    
        io.in.ready := stateReg === empty
        io.out.valid := stateReg === full
    
        when(stateReg === empty) {
            when(io.in.valid) {
                dataReg := io.in.data
                stateReg := full
            }
        }.otherwise {
            when(io.out.ready) {
                stateReg := empty
            }
        }
    
        io.out.data := dataReg
    }
    
    • 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

    它的输入接口还是Channel类型的,输出是Channel接口的翻转。Buffer包含一个小型的状态机来指示emptyfull这两种状态。Buffer驱动的握手信号in.readyout.valid取决于状态寄存的值。

    当状态为empty时,且输入端的数据是valid的,我们可以寄存数据并将状态切换为full;如果状态为full,且下游的接收器是ready的,那么向下游的数据传输就会发生,状态又会切换回empty

    基于单字节缓冲区的发送端实现

    现在我们就可以基于上面的Buffer来实现带缓冲区的发送端了,BufferedTx的实现如下:

    class BufferedTx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val txd = Output(Bits(1.W))
            val channel = new Channel()
        })
    
        val tx = Module(new Tx(frequency, baudRate))
        val buf = Module(new Buffer())
    
        buf.io.in <> io.channel
        tx.io.channel <> buf.io.out
        io.txd <> tx.io.txd
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这个BufferedTxTx前连接了一个单Buffer的组合。现在有了缓冲区之后,就解决了之前Tx只能ready一个周期的问题了。单字缓冲区到真实FIFO的拓展也可以在不修改发送端或单字节缓冲区的情况下轻松实现。

    接收端实现

    接下来是接收端(Rx)的实现,接收端的实现更有技巧性,因为它需要重新构建串行数据的时序。Rx的Chisel实现如下:

    class Rx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val rxd = Input(Bits(1.W))
            val channel = Flipped(new Channel())
        })
    
        val BIT_CNT = ((frequency + baudRate / 2) / baudRate -1).U
        // 接收器在起始位下降沿的1.5个比特时间后开始接收数据
        val START_CNT = ((3 * frequency / 2 + baudRate / 2) / baudRate -1).U
    
        // 同步异步的RX数据
        // 复位时会复位为1以停止读数据
        val rxReg = RegNext(RegNext(io.rxd, 1.U), 1.U)
    
        val shiftReg = RegInit('A'.U(8.W))
        val cntReg = RegInit(0.U(20.W))
        val bitsReg = RegInit(0.U(4.W))
        val valReg = RegInit(false.B)
    
        when(cntReg =/= 0.U) {
            cntReg := cntReg - 1.U
        }.elsewhen(bitsReg =/= 0.U) {
            cntReg := BIT_CNT
            shiftReg := Cat(rxReg, shiftReg >> 1)
            bitsReg := bitsReg - 1.U
            // 移入最后一位时
            when(bitsReg === 1.U) {
                valReg := true.B
            }
        }.elsewhen(rxReg === 0.U) {
            cntReg := START_CNT
            bitsReg := 8.U
        }
    
        when(valReg && io.channel.ready) {
            valReg := false.B
        }
    
        io.channel.data := shiftReg
        io.channel.valid := valReg
    }
    
    • 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

    代码中的START_CNT的意义是,接收器等待起始位的下降沿,而接收器在起始位下降沿的1.5个比特时间后开始接收数据。之后的每个比特时间内,都会将接收到的位移入寄存器。因此代码中有两个等待时间,一个是START_CNT,一个是BIT_CNT,不过两个时间用的都是同一个计数器cntReg。移入全部8个比特之后,valReg会给出一个有效的字节。

    串口通信“Hello World”的实现

    最后就是用BufferedTxRx实现串口通信了,具体实现如下:

    class Comm(frequency: Int, baudRate: Int) extends Module {
        val tx = Module(new BufferedTx(frequency, baudRate))
        val rx = Module(new Rx(frequency, baudRate))
    
        val msg = "Hello World!"
        val text = VecInit(msg.map(_.U))
        val len = msg.length().U
    
        val cntReg = RegInit(0.U(8.W))
    
        tx.io.channel.data := text(cntReg)
        tx.io.channel.valid := cntReg =/= len
        rx.io.channel.ready := tx.io.channel.ready
        rx.io.rxd := tx.io.txd
    
        when(tx.io.channel.ready && cntReg =/= len) {
            cntReg := cntReg + 1.U
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这一部分没什么好解释的,就是把txtx连接起来,然后Comm生成数据给txtxrx之间进行串口通信。

    完整的实现代码如下,注意,其中加入了用于调试和测试的时钟计数器和输出函数:

    import chisel3._
    import chisel3.util._
    
    class Channel extends Bundle {
        val data = Input(Bits(8.W))
        val ready = Output(Bool())
        val valid = Input(Bool())
    }
    
    class Tx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val txd = Output(Bits(1.W))
            val channel = new Channel()
        })
    
        val clkReg = RegInit(0.U(16.W))
        clkReg := clkReg + 1.U
    
        val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).asUInt
    
        val shiftReg = RegInit(0x7ff.U) // 共11位,一个开始位,两个结束位
        val cntReg = RegInit(0.U(20.W))
        val bitsReg = RegInit(0.U(4.W))
    
        io.channel.ready := (cntReg === 0.U) && (bitsReg === 0.U)
        io.txd := shiftReg(0)
    
        when(cntReg === 0.U) {
            cntReg := BIT_CNT
            when(bitsReg =/= 0.U) {
                val shift = shiftReg >> 1
                shiftReg := Cat(1.U, shift(9, 0))
                bitsReg := bitsReg - 1.U
                printf("%d: sending ...%b\n", clkReg, shiftReg(1))
            }.otherwise {
                when(io.channel.valid) {
                    // 一个起始位0和两个结束位11
                    shiftReg := Cat(Cat(3.U, io.channel.data), 0.U)
                    printf("%d: sending start 0 ...%b\n", clkReg, 0.U)
                    bitsReg := 11.U
                }.otherwise {
                    shiftReg := 0x7ff.U
                }
            }
        }.otherwise {
            cntReg := cntReg - 1.U
        }
    }
    
    class Buffer extends Module {
        val io = IO(new Bundle {
            val in = new Channel()
            val out = Flipped(new Channel())
        })
    
        val empty :: full :: Nil = Enum(2)
        val stateReg = RegInit(empty)
        val dataReg = RegInit(0.U(8.W))
    
        io.in.ready := stateReg === empty
        io.out.valid := stateReg === full
    
        when(stateReg === empty) {
            when(io.in.valid) {
                dataReg := io.in.data
                stateReg := full
            }
        }.otherwise {
            when(io.out.ready) {
                stateReg := empty
            }
        }
    
        io.out.data := dataReg
    }
    
    class BufferedTx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val txd = Output(Bits(1.W))
            val channel = new Channel()
        })
    
        val tx = Module(new Tx(frequency, baudRate))
        val buf = Module(new Buffer())
    
        buf.io.in <> io.channel
        tx.io.channel <> buf.io.out
        io.txd <> tx.io.txd
    }
    
    class Rx(frequency: Int, baudRate: Int) extends Module {
        val io = IO(new Bundle {
            val rxd = Input(Bits(1.W))
            val channel = Flipped(new Channel())
        })
    
        val clkReg = RegInit(0.U(16.W))
        clkReg := clkReg + 1.U
    
        val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).U
        // 接收器在起始位下降沿的1.5个比特时间后开始接收数据
        val START_CNT = ((3 * frequency / 2 + baudRate / 2) / baudRate - 1).U
    
        // 同步异步的RX数据
        // 复位时会复位为1以停止读数据
        val rxReg = RegNext(RegNext(io.rxd, 1.U), 1.U)
    
        val shiftReg = RegInit('A'.U(8.W))
        val cntReg = RegInit(0.U(20.W))
        val bitsReg = RegInit(0.U(4.W))
        val valReg = RegInit(false.B)
    
        when(cntReg =/= 0.U) {
            cntReg := cntReg - 1.U
        }.elsewhen(bitsReg =/= 0.U) {
            cntReg := BIT_CNT
            shiftReg := Cat(rxReg, shiftReg >> 1)
            bitsReg := bitsReg - 1.U
            printf("%d: reading ...%b\n", clkReg, rxReg)
            // 移入最后一位时
            when(bitsReg === 1.U) {
                valReg := true.B
                printf("Done reading ...%c\n", Cat(rxReg, shiftReg >> 1))
            }
        }.elsewhen(rxReg === 0.U) {
            cntReg := START_CNT
            bitsReg := 8.U
            printf("%d: reading start 0 ...%b\n", clkReg, rxReg)
        }
    
        when(valReg && io.channel.ready) {
            valReg := false.B
        }
    
        io.channel.data := shiftReg
        io.channel.valid := valReg
    }
    
    class Comm(frequency: Int, baudRate: Int) extends Module {
        val tx = Module(new BufferedTx(frequency, baudRate))
        val rx = Module(new Rx(frequency, baudRate))
    
        val msg = "Hello World!"
        val text = VecInit(msg.map(_.U))
        val len = msg.length().U
    
        val cntReg = RegInit(0.U(8.W))
    
        tx.io.channel.data := text(cntReg)
        tx.io.channel.valid := cntReg =/= len
        rx.io.channel.ready := tx.io.channel.ready
        rx.io.rxd := tx.io.txd
    
        when(tx.io.channel.ready && cntReg =/= len) {
            cntReg := cntReg + 1.U
        }
    }
    
    • 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
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

    测试代码如下:

    import chisel3._
    import chiseltest._
    import org.scalatest.flatspec.AnyFlatSpec
    
    class CommTest extends AnyFlatSpec with ChiselScalatestTester {
        "Comm" should "pass" in {
            test(new Comm(2000, 80)) { dut =>
                dut.clock.setTimeout(0)
                for (i <- 0 until 4000) {
                    dut.clock.step(1)
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出如下:

        25: sending start 0 ...0
        28: reading start 0 ...0
        50: sending ...0
        66: reading ...0
        75: sending ...0
        91: reading ...0
       100: sending ...0
       116: reading ...0
       125: sending ...1
       141: reading ...1
       150: sending ...0
       166: reading ...0
       175: sending ...0
       191: reading ...0
       200: sending ...1
       216: reading ...1
       225: sending ...0
       241: reading ...0
    Done reading ...H
       250: sending ...1
       275: sending ...1
       300: sending ...1
       325: sending start 0 ...0
       328: reading start 0 ...0
       350: sending ...1
       366: reading ...1
       375: sending ...0
       391: reading ...0
       400: sending ...1
       416: reading ...1
       425: sending ...0
       441: reading ...0
       450: sending ...0
       466: reading ...0
       475: sending ...1
       491: reading ...1
       500: sending ...1
       516: reading ...1
       525: sending ...0
       541: reading ...0
    Done reading ...e
       550: sending ...1
       575: sending ...1
       600: sending ...1
       625: sending start 0 ...0
       628: reading start 0 ...0
       650: sending ...0
       666: reading ...0
       675: sending ...0
       691: reading ...0
       700: sending ...1
       716: reading ...1
       725: sending ...1
       741: reading ...1
       750: sending ...0
       766: reading ...0
       775: sending ...1
       791: reading ...1
       800: sending ...1
       816: reading ...1
       825: sending ...0
       841: reading ...0
    Done reading ...l
       850: sending ...1
       875: sending ...1
       900: sending ...1
       925: sending start 0 ...0
       928: reading start 0 ...0
       950: sending ...0
       966: reading ...0
       975: sending ...0
       991: reading ...0
      1000: sending ...1
      1016: reading ...1
      1025: sending ...1
      1041: reading ...1
      1050: sending ...0
      1066: reading ...0
      1075: sending ...1
      1091: reading ...1
      1100: sending ...1
      1116: reading ...1
      1125: sending ...0
      1141: reading ...0
    Done reading ...l
      1150: sending ...1
      1175: sending ...1
      1200: sending ...1
      1225: sending start 0 ...0
      1228: reading start 0 ...0
      1250: sending ...1
      1266: reading ...1
      1275: sending ...1
      1291: reading ...1
      1300: sending ...1
      1316: reading ...1
      1325: sending ...1
      1341: reading ...1
      1350: sending ...0
      1366: reading ...0
      1375: sending ...1
      1391: reading ...1
      1400: sending ...1
      1416: reading ...1
      1425: sending ...0
      1441: reading ...0
    Done reading ...o
      1450: sending ...1
      1475: sending ...1
      1500: sending ...1
      1525: sending start 0 ...0
      1528: reading start 0 ...0
      1550: sending ...0
      1566: reading ...0
      1575: sending ...0
      1591: reading ...0
      1600: sending ...0
      1616: reading ...0
      1625: sending ...0
      1641: reading ...0
      1650: sending ...0
      1666: reading ...0
      1675: sending ...1
      1691: reading ...1
      1700: sending ...0
      1716: reading ...0
      1725: sending ...0
      1741: reading ...0
    Done reading ... 
      1750: sending ...1
      1775: sending ...1
      1800: sending ...1
      1825: sending start 0 ...0
      1828: reading start 0 ...0
      1850: sending ...1
      1866: reading ...1
      1875: sending ...1
      1891: reading ...1
      1900: sending ...1
      1916: reading ...1
      1925: sending ...0
      1941: reading ...0
      1950: sending ...1
      1966: reading ...1
      1975: sending ...0
      1991: reading ...0
      2000: sending ...1
      2016: reading ...1
      2025: sending ...0
      2041: reading ...0
    Done reading ...W
      2050: sending ...1
      2075: sending ...1
      2100: sending ...1
      2125: sending start 0 ...0
      2128: reading start 0 ...0
      2150: sending ...1
      2166: reading ...1
      2175: sending ...1
      2191: reading ...1
      2200: sending ...1
      2216: reading ...1
      2225: sending ...1
      2241: reading ...1
      2250: sending ...0
      2266: reading ...0
      2275: sending ...1
      2291: reading ...1
      2300: sending ...1
      2316: reading ...1
      2325: sending ...0
      2341: reading ...0
    Done reading ...o
      2350: sending ...1
      2375: sending ...1
      2400: sending ...1
      2425: sending start 0 ...0
      2428: reading start 0 ...0
      2450: sending ...0
      2466: reading ...0
      2475: sending ...1
      2491: reading ...1
      2500: sending ...0
      2516: reading ...0
      2525: sending ...0
      2541: reading ...0
      2550: sending ...1
      2566: reading ...1
      2575: sending ...1
      2591: reading ...1
      2600: sending ...1
      2616: reading ...1
      2625: sending ...0
      2641: reading ...0
    Done reading ...r
      2650: sending ...1
      2675: sending ...1
      2700: sending ...1
      2725: sending start 0 ...0
      2728: reading start 0 ...0
      2750: sending ...0
      2766: reading ...0
      2775: sending ...0
      2791: reading ...0
      2800: sending ...1
      2816: reading ...1
      2825: sending ...1
      2841: reading ...1
      2850: sending ...0
      2866: reading ...0
      2875: sending ...1
      2891: reading ...1
      2900: sending ...1
      2916: reading ...1
      2925: sending ...0
      2941: reading ...0
    Done reading ...l
      2950: sending ...1
      2975: sending ...1
      3000: sending ...1
      3025: sending start 0 ...0
      3028: reading start 0 ...0
      3050: sending ...0
      3066: reading ...0
      3075: sending ...0
      3091: reading ...0
      3100: sending ...1
      3116: reading ...1
      3125: sending ...0
      3141: reading ...0
      3150: sending ...0
      3166: reading ...0
      3175: sending ...1
      3191: reading ...1
      3200: sending ...1
      3216: reading ...1
      3225: sending ...0
      3241: reading ...0
    Done reading ...d
      3250: sending ...1
      3275: sending ...1
      3300: sending ...1
      3325: sending start 0 ...0
      3328: reading start 0 ...0
      3350: sending ...1
      3366: reading ...1
      3375: sending ...0
      3391: reading ...0
      3400: sending ...0
      3416: reading ...0
      3425: sending ...0
      3441: reading ...0
      3450: sending ...0
      3466: reading ...0
      3475: sending ...1
      3491: reading ...1
      3500: sending ...0
      3516: reading ...0
      3525: sending ...0
      3541: reading ...0
    Done reading ...!
      3550: sending ...1
      3575: sending ...1
      3600: sending ...1
    [info] CommTest:
    [info] Comm
    [info] - should pass
    [info] Run completed in 3 seconds, 342 milliseconds.
    [info] Total number of tests run: 1
    [info] Suites: completed 1, aborted 0
    [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
    [info] All tests passed.
    
    • 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
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272

    测试通过,这里的时序是值得研究一下的,这里就不展开讲了。

    结语

    这一篇文章首先讲述了串口的概念,对实现串口通信的最小系统做了简单阐述,然后从发送端开始,实现了基于单字节FIFO缓冲区的发送端,接着类似地实现了接收端,最后将二者综合在一起,实现了发送“Hello World!”信息的串口通信demo。可以看到,到这里代码量相对之前来说多了很多,但是因为模块化设计且利用了Chisel的特性,因此条理也很清晰。下一篇文章,我们将继续实现FIFO的几种变体,进一步实践Chisel开发。

  • 相关阅读:
    MFC上下文菜单与定时器学习笔记
    基于spingboot的websocket订阅、广播、多人聊天室示例
    在CorelDraw中,VBA宏调用是如何执行的?
    【Text2SQL 论文】How to prompt LLMs for Text2SQL
    Shell速成:快速提升你的Linux命令行技能
    搭建Android自动化python+appium环境
    JDK8新特性,Optional工具类的简单实用场景
    微信小程序数据传递的综合指南
    一键更新图像或表格号
    21-CSS中的3D属性
  • 原文地址:https://blog.csdn.net/weixin_43681766/article/details/126266799