CPU自制入门1.9 I/O 的设计与实现_CPU自制入门1.9 I/O 的设计与实现试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > CPU自制入门 > 1.9 I/O 的设计与实现

CPU自制入门——1.9 I/O 的设计与实现

本节讲解I/O 的设计与实现。本节要设计的I/O 有三种,分别是测量时间用的定时器、串口通信规范UART(Universal Asynchronous Receiver Transmitter),以及控制LED、开关用的GPIO(General Purpose Input Output)。它们都是最基本的I/O,几乎所有计算机都配有全部三种或其中一部分I/O 接口。 1.9.1  定时器 什么是定时器 定时器是用来测量时间的装置。和我们日常使用的厨房定时器、起床闹钟的功能完全相同。计算机可以利用定时器实现时间测量、周期性处理、超时判断等许多用途。定时器通过软件设置定时的时长并启动,经过设定时间后引发CPU 中断请求。 定时器的设计 我们将要设计的定时器具有两种动作模式:一种是经过设定时间后向CPU 请求一次中断即完成操作的单次定时模式,另一种是每经过设定时间就向CPU 请求一次中断的循环定时模式。单次定时器在只进行一次时间测量时使用,循环定时器在需要执行周期性操作时使用。 通常,I/O 都带有功能多样的控制寄存器,有内存映射的I/O 的控制寄存器还分配有访问地址。CPU 通过访问I/O 的控制寄存器对I/O 进行控制。我们这里设计的定时器的控制寄存器的规格如表1-57 所示。 表1-57 定时器控制寄存器 控制寄存器0 :控制寄存器 [0] :起始位(S) 该位用来控制定时器的开/ 关。该位为1 时定时器开始计数。 [1] :模式位(M) 该位用来设置定时器的动作模式。该位为1 时定时器为循环定时模式。 控制寄存器1 :中断寄存器 [0] :中断位(I) 当定时器计数达到设定的最大值时该位变为1。该位为1 时向CPU 发送中断请求。 控制寄存器2 :最大值寄存器 [31:0] :最大值(EXPR_VAL) 该寄存器用来设置计数的最大值。如果计数器累计到与该寄存器的值相等时, 表示定时时间到。 控制寄存器3 :计数器寄存器 [31:0] :计数器(COUNTER) 该寄存器为定时器的计数器。计时开始后该寄存器的值开始增长。 定时器的实现 我们设计的定时器由一个被称为timer 的模块构成。定时器的框图如图1-115 所示。该模块由记录经过时间计数器(COUNTER)、表示定时时间的计数最大值寄存器(EXPR_VAL)、控制定时器启动和动作模式的起始位(S)与模式位(M)以及通知计时完成的中断位(I)构成。 图1-115 定时器框图 起始位设为1 时计数开始,计数到达最大值后计数器被初始化,并设置中断位为1。此时如果模式位为单次定时模式,则会将起始位清零。如果模式位为循环定时模式,则自动进入下一个计数周期。定时器的宏一览如表1-58 所示,信号线一览如表1-59 所示, 源程序如代码1-37 所示。 表1-58 宏一览(timer.h) 表1-59 信号线一览(timer.v) 代码1-37 定时器控制逻辑(timer.v) [Ⅰ]计时完成标志位的生成 起始位(start)为有效,且计数器(counter)值等于计数最大值(expr_val) 时,计时完成标志位(expr_flag)为高电平。 [Ⅱ]异步复位 复位信号(reset)有效时对寄存器进行初始化。初始化时全部控制信号设为无效,数据信号设为0。 [Ⅲ]就绪信号的生成 由于片选信号(cs_)与地址选通信号(as_)同时有效时进行总线访问操作, 就绪信号(rdy_)设为有效。其他情况时就绪信号(rdy_)为无效。 [Ⅳ]读取访问 片选信号(cs_)与地址选通信号(as_)同时有效、且读/ 写信号(rw)为读取(READ)时,发生读取访问操作。(1)处对地址信号(rd_data)进行解码,并将对应控制寄存器的值输出到数据读取信号(rd_data)。(2)处针对无访问情况下, 向数据读取信号输出0。 [Ⅴ]写入访问 片选信号(cs_)与地址选通信号(as_)同时有效,且读/ 写信号(rw)为写入(WRITE)时,向地址信号(addr)对应的寄存器写入数据。(3)、(6)、(7)、(8)处对控制寄存器进行写入操作。 (4)处对起始位(start)进行控制。定时器动作模式(mode)为单次定时模式(TIMER_MODE_ONE_SHOT)时,计时完成后将起始位(start)清零。 (5)处对中断请求(irq)进行控制。中断请求(irq)比写入控制寄存器1 操作优先级高。这样设计是为了防止在写入同时计时结束的情况发生时,无法获取来自定时器的中断请求。 (9) 处在计时完成时将计数器(counter) 初始化为0。(10) 处对计数器(counter)进行累加。计数器在定时器启动,且未达计数最大值时进行累加操作。 1.9.2  UART 什么是UART UART 是起止式同步接收、发送串口通信装置。UART 是一种使用收、发两根信号线进行串口通信的标准。数据一位接一位地传输的方式称为串口通信。图1-116 所示的是UART 通信的波形图。 图1-116 UART通信波形图 起止式同步通信是指在发送的数据前添加表示通信开始的起始位(L)、在数据末尾添加表示通信结束的停止位(H)的通信方式。空闲时总是输出停止位。数据从LSB 一端开始按顺序输出,并可以选择添加奇偶校验位,最后输出停止位。数据传输单位为7 位或8 位。 UART 的通信速率用波特率(baud rate)来表示。波特率指的是信号被调制以后的变化率,即单位时间内载波变化的次数。用于波特率计算的信号除了数据位,也包含起始位、停止位、奇偶校验位,因此波特率与单纯的数据传输速率是不同的。UART 常用的波特率有9 600 baud、19 200 baud、38 400 baud 等。 专栏 UART 实例 计算机背面面板通常有一个与VGA 端口相似的9 针接口(DE-9 接头),如图1-117 所示。这个端口被称为RS-232(Recommended Standard 232)或串口,是一种UART 标准的实现。RS-232 是可以用来连接调制解调器等计算机周边设备,或者作为控制台接口①。 ① Console 接口是网络设备用来与计算机或终端设备进行连接的常用接口。——译者注 UART的设计 以下为我们将要设计的UART 规格:波特率为38 400 baud、数据为8 比特、无奇偶校验、停止位为1 比特。UART 的控制寄存器如表1-60 所示。 表1-60 UART 控制寄存器 控制寄存器0 :状态寄存器 [0] :接收完成中断位(RI) 该位在数据发送完成时被设置为高电平,并同时向CPU 发送中断请求。 [1] :发送完成中断位(TI) 该位在数据接收完成时被设置为高电平,并同时向CPU 发送中断请求。 [2] :接收中标志位(RB) 该位在数据接收时为高电平。 [3] :发送中标志位(TB) 该位在数据发送时为高电平。 控制寄存器1 :收发数据寄存器 [7:0] :收发的数据(DATA) 当向该寄存器写入的数据时,数据会通过UART 发送。当读取该寄存器时, 会将接收到的数据读取出来。 UART 的实现 我们设计的UART 由表1-61 所示的模块构成。UART 的框图如图1-118 所示。UART 由发送模块、接收模块、控制模块,以及进行整体连接的顶层模块构成。UART 使用的宏一览如表1-62 所示。 表1-61 UART 的模块 图1-118 UART的框图 表1-62 宏一览(uart.h) 发送模块 发送模块设计为一个有限状态机。发送模块的状态迁移图如图1-119 所示。该模块的状态有“空闲状态”和“发送状态”两种。处于发送状态时,依据比特计数器对下一个发送的数据进行控制。 图1-119 发送模块的状态迁移图 发送模块在空闲状态时,如果发送开始信号到来,则保存发送的数据并开始发送。发送模块基于输入的时钟生成需要的波特率。相对于UART 常用的波特率,例如38 400 baud,向电路输入的时钟高达数十MHz,因此需要对输入的时钟进行分频来生成波特率。时钟的分频比率计算公式为“ 输入时钟频率÷ 波特率”。分频使用计数器来得到需要的频率。 发送状态时,每当分频计数器计满预定次数时、发送下一个比特数据。发送状态中, 依次发送起始位、数据0~7 位、停止位,最后将比特计数器清零并返回空闲状态。发送完毕后输出发送完成信号。发送信号时模块输出忙信号。 发送模块的信号线如表1-63 所示,源程序如代码1-38 所示。 表1-63 信号线一览(uart_tx.v) 代码1-38 UART 发送模块(uart_tx.v) [Ⅰ]发送中标志信号的生成 当UART 模块状态为发送(UART_STATE_TX)时,发送中标志信号(tx_ busy)有效。 [Ⅱ]异步复位 复位信号(reset)有效时,进行寄存器初始化操作。 [Ⅲ]空闲状态 (1)处在空闲状态时,如果开始信号(tx_start)到来,则将发送的数据(tx_ data)保存到移位寄存器(sh_reg)、输出开始位、状态迁移到发送状态(UART_ STATE_TX)。 [Ⅳ]发送状态 (3)和(8)处对波特率进行控制。分频用计数器(div_cnt)使用倒数方式(8),当值倒数到0 时发送下一比特数据(3)。(4)处对发送的比特数据进行控制。因为在迁移到发送状态(UART_STATE_TX)时开始发送起始位,所以此处发送剩下的数据位和停止位。 (5)处在发送完数据的MSB(第七位)后,发送停止位并递增比特计数器(bit_cnt)。(6)处当停止位发送完毕,比特计数器(bit_cnt)归零初始化,使能发送完成信号(tx_end),并将状态迁移到空闲状态(UART_STATE_IDLE)。最后剩下的一种情况为数据的发送,(7)处发送数据比特,并递增比特计数器(bit_ cnt)。因为数据从LSB 开始发送,所以在发送的同时移位寄存器(sh_reg)向右移动一位。 接收模块 UART 的接收部分使用比波特率高的采样频率实现。图1-120 展示了UART 接收时的示例。 图1-120 UART接收示例 [1]起始位的检测 接收信号转为L 时视为检测到起始位。 [2]起始位的中心 在检测出起始位时开始测量波特率的半周期,以确定起始位的中心。 [3]数据接收开始 从起始位的中心来计算波特率的1 个周期位置,然后从LSB 开始接收数据。 [4] 数据接收完成 数据接收到MSB 后,则表示数据接收完成。 [5]停止位的接收 数据接收完成后,最后接收停止位。 检测接收信号时,使用比波特率更高的频率进行采样,由于检测到起始位的同时开始同步(起止式同步)传输,因此没有专门的同步信号也可以传输数据。为了准确接收数据,采样时钟必须具有比波特率充分高的频率。一般使用比波特率高16 倍的时钟进行采样。采用这个频率是因为最早开发的UART 测量芯片使用了16 倍的采样时钟。 接收模块和发送模块一样,也是基于有限状态机制作的。接收模块的状态迁移图如图1-121 所示。该模块有空闲和接收两个状态,接收状态下依据比特计数器控制数据的接收。 图1-121 接收模块的状态迁移图 接收模块在空闲状态下检测到起始位后,开始接收信号。此时,分频计数器中记入波特率的半周期。第一次分频计数器计数满时的位置为起始位的中心。此后每过一个周期接收一个数据位。 接收状态下,依次接收起始位、数据1~8 位、停止位。当正确接收到停止位(H) 后使能接收完成信号、将接收到的数据输出、为下次接收数据将分频计数器设置为半周期,并返回空闲状态。 接收到的停止位为错误的值(L)时称为帧错误(Framing Error),此时将接收到的数据废弃并返回空闲状态。帧错误是指帧的同步不成功的状态。接收信号时模块输出忙信号。 接收模块的信号线一览如表1-64 所示,源程序如代码1-39 所示。 表1-64 信号线一览(uart_rx.v) 代码1-39 UART 接收模块(uart_rx.v) [Ⅰ]接收中标志信号的生成 当UART 模块状态为接收时,接收中标志信号(rx_busy)有效。 [Ⅱ]异步复位 复位信号(reset)有效时,进行寄存器初始化操作。 [Ⅲ]空闲状态 (1)处在空闲状态下检测到起始位时,转移到接收状态(UART_STATE_RX)。(2)处在数据接收完毕、使能接收完成信号(rx_end)后,对接收完成信号进行清除。 [Ⅳ]接收状态 (3)和(8)处对波特率进行控制。分频用计数器(div_cnt)使用倒数方式(8),当值倒数到0 时接收下一比特数据(3)。(4)处对接收的比特数据进行控制。 在(5)处接收到停止位后,进行以下接收完成后的处理:清零比特计数器(bit_cnt)、为接收下一数据设置分频计数器(div_cnt)为半周期、转移到空闲状态(UART_STATE_IDLE)。 (6)处在接收完成时对帧错误进行检测。当停止位为H 之外的错误值时,视为发生帧错误。当停止位为H 时,生成接收完成信号(rx_end)。 (7)处接收数据比特并递增比特计数器(bit_cnt)。移位寄存器(sh_reg)向右移位1 比特、将接收到的数据插入MSB。因为从LSB 端开始依次接收数据,最初接收的数据经过不断移位,最终将移位到LSB 的位置。 控制模块 UART 的控制模块用来控制UART 的信号收发和控制寄存器的访问。UART 控制模块的信号线一览如表1-65 所示,源程序如代码1-40 所示。 表1-65 信号线一览(uart_ctrl.v) 代码1-40 UART 控制模块(uart_ctrl.v) [Ⅰ]异步复位 复位信号(reset)有效时,进行寄存器初始化操作。 [Ⅱ]就绪信号的生成 当片选信号(cs_)和地址选通信号(as_)同时到来时开始总线访问,使能就绪信号(rdy_)。其他情况下就绪信号(rdy_)无效。 [Ⅲ]读取访问 当片选信号(cs_)和地址选通信号(as_)同时到来,且读/ 写信号(rw)为读取(READ)时开始读取访问。(1)处的case 语句对地址进行解码,地址(addr) 对应的控制寄存器的值会作为读出的数据(rd_data)被输出。(2)处读取控制寄存器0,(3)处读取控制寄存器1。控制寄存器1 的收发数据从接收缓冲区(rx_buf) 读取。(4)处在无访问时向读取的数据信号(rd_data)输出0。 [Ⅳ]写入访问 当片选信号(cs_)和地址选通信号(as_)同时到来,且读/ 写信号(rw)为写入(WRITE)时,对地址信号(addr)指向的控制寄存器写入数据。(6)和(8)处对寄存器0 进行写入操作。 (5)处对发送完成中断进行控制。发送完成时的处理比写入控制寄存器0 的操作优先级高。(7)处对接收完成中断进行控制。接收完成时的处理比写入控制寄存器0 的操作优先级高。(5)和(7)处的中断比来自总线的写入访问优先处理,是为了在防止写入的同时发送完成的情况下丢失中断。 (9)处对控制寄存器1 进行写入操作。当有数据写入控制寄存器1 时,写入的数据作为发送的数据(tx_data)输出,同时输出发送开始信号(tx_start)(10)。无写入时在(11)处清除发送的数据(tx_data)与发送开始信号(tx_start)。当接收完成信号(rx_end)有效时,在(12)处将接收的数据(rx_data)放入接收用数据缓冲区(rx_buf)。 顶层模块 顶层模块用来连接发送模块、接收模块和控制模块。顶层模块的端口连接图如图1-122 所示。控制模块与总线接口、中断请求信号相连接。控制模块与发送模块、控制模块与接收模块间通过控制信号相连接。发送模块与UART 的发送信号相连、接收模块与UART 接收信号相连。 图1-122 UART顶层模块端口连接图181 I/O 的设计与实现 1.9 1.9.3  GPIO 什么是GPIO GPIO(General Purpose Input Output)是以位为单位进行数字输入输出的I/O 接口。作为单纯的通用输入输出I/O,输入时从外部读取输入信号、输出时将写入的值输入到外部。GPIO 可以与各种设备相连接。例如可以连接图1-123 中的LED、开关等。 图1-123 GPIO 连接示意图 GPIO 的设计 我们设计的GPIO 有输入专用端口、输出专用端口,以及可以输入输出的双向端口三种。输出输入端口可以作为输入或输出端口使用,输入输出的方向通过控制寄存器设置。 GPIO 控制寄存器如表1-66 所示。 表1-66 GPIO 的控制寄存器 控制寄存器0 :输入端口 [31:0] :输入数据(INPUT_DATA) 通过对该寄存器的读取,可以读取输入端口的信号值。 控制寄存器1 :输出端口 [31:0] :输出数据(OUTPUT_DATA) 将数据写入该寄存器,会直接输出到输出端口。 控制寄存器2 :输入输出端口 [31:0] :输入输出数据(INOUT_DATA) 当输入输出方向为输入时,读取该寄存器即可获取外部输入的信号值。当输入输出方向为输出时,对该寄存器写入数据即可输出到外部。 控制寄存器3 :输入输出方向 [31:0] :输入输出方向(INOUT_DIR) 该寄存器用来设置输入输出端口的信号方向。当寄存器值为0 时端口为输入、值为1 时端口为输出。该寄存器各个比特对应控制相应的输入输出端口。 GPIO 的实现 我们设计的GPIO 由名为gpio 的模块构成。GPIO 框图如图1-124 所示。 图1-124 GPIO框图 GPIO 由输入端口、输出端口和输入输出端口三个部分构成。各个端口在设计上是独立的,每个端口是否实现,以及每个端口的通道数都使用宏来定义。GPIO 的宏一览如表1-67 所示、信号线如表1-68 所示。 表1-67 宏一览(gpio.h) 表1-68 信号线一览(gpio.v) 输入输出端口使用三态门实现。三态门(也称为三态缓冲器)是一种可以输出H、L 以及高阻状态的电路结构。高阻状态是指电气上绝缘(断路)的状态。 三态门除了有输入、输出信号之外,还有决定是否驱动输出的输出使能信号。输出使能信号有效时,三态门的输出由输入信号驱动。输出使能信号无效时,三态门的输出为高阻状态。 图1-125 为三态门的真值表。输出使能信号为正逻辑。 图1-125 三态门的真值表 方向为输出的输入输出端口如图1-126 所示、方向为输入的输入输出端口如图1-127 所示。 图1-126 方向为输出的输入输出端口        图1-127 方向为输入的输入输出端口 输入输出端口的内部由输入的数据、输出的数据和输出使能3 个信号构成。用作输出时将输出使能信号设为有效、并向输入输出端口发送输出的数据。此时从输入的数据可以读取到和输出的数据相同的值;用作输入时将输出使能信号设为无效、输出用三态门变为高阻状态。由于此时端口被外部设备驱动,来自外部的输入值可以作为输入的数据读取。控制输入输出端口的源程序如代码1-41 所示、信号线的对应参见图1-128。 代码1-41 GPIO 的输入输出端口(gpio.v) [Ⅰ]输入输出信号的定义 此处定义输入输出端口所用的信号。输入输出端口信号的对应如图1-128 所示。由于inout 类型端口有只能使用网络类型的限制,因此我们定义名为io 的reg 型变量,并连续赋值给输入输出端口(gpio_io)。 [Ⅱ]输入输出信号的连续赋值 当前端口(gpio_io)的值连续赋值给输入的数据(io_in)。用作输出时值为来自内部的输出数据,用作输入时值为来自外部的输入数据。输入输出端口(gpio_ io)的值由输入输出信号(io)连续赋值得到。 [Ⅲ]输入输出方向的控制 此处切换输出与高阻状态。(1)处的for 循环遍历所有输入输出端口并执行assign 语句。用作输入时(2)处为高阻状态,用作输入时(2)处代入输出的数据。Verilog HDL 中高阻状态用z 表示。 图1-128 输入输出端口的信号 接下来,我们来说明总线访问的控制部分。GPIO 的总线访问控制部分的源程序如代码1-42 所示。 代码1-42 GPIO 的总线访问控制(gpio.v) [Ⅰ]异步复位 复位信号(reset)有效时,进行寄存器初始化操作。初始化时,全部控制信号设为无效、数据信号输出0。 [Ⅱ]就绪信号的生成 当片选信号(cs_)和地址选通信号(as_)同时到来时,表示有来自总线的访问,使能就绪信号(rdy_)。其他情况下就绪信号(rdy_)无效。 [Ⅲ]读取访问 当片选信号(cs_)和地址选通信号(as_)同时到来,且读/ 写信号(rw)为读取(READ)时,表示即将进行读取访问。(1)处的case 对地址信号(addr)解码,将地址指向的控制寄存器的值输出到读取的数据(rd_data)。(2)处读取输入端口(gpio_in)。(3)处读取输出端口(gpio_out)。(4)处读取输入输出端口(gpio_io)和输入输出方向(io_dir)。(5)处在无访问情况时向输出的数据(rd_ data)输出0。 [Ⅳ]写入访问 当片选信号(cs_)和地址选通信号(as_)同时到来,且读/ 写信号(rw)为写入(WRITE)时,向地址信号(addr)所指向的控制寄存器写入数据。(6)处的case 语句对地址信号(addr)解码,对地址所指向的控制寄存器进行写入操作。(7)处向输出端口(gpio_out)写入。(8)处向输入输出端口的输出(io_out)和输入输出方向(io_dir)写入。 1.9.4  小结 本节对定时器、UART 和GPIO 的设计与实现进行了说明。每个都是基本的输入输出模块,大多数计算机都搭载了这些功能。通过制作基本的输入输出模块,读者们可以深入理解输入输出模块的动作、原理、控制方法以及使用方法。 专栏 I/O 相关书籍 組み込みI/O インタフェース(宇野俊夫、翔泳社)(中文译名:《I/O 接口结构》) 本书深入浅出地讲解了I/O 的接口、功能、用途、结构等内容。还讲解了I/O 接口的访问时序、数据手册的阅读等实践内容,涉及范围广泛。推荐设计I/O 的硬件技术者,以及使用I/O 的软件工程师们阅读学习。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《CPU自制入门》其他试读目录

• 1.1 序
• 1.2 计算机系统
• 1.3 数字电路基础
• 1.4 Verilog HDL 语言
• 1.5 系统蓝图
• 1.6 总线的设计与实现
• 1.7 存储器的设计与实现
• 1.8 AZ Processor 的设计与实现
• 1.9 I/O 的设计与实现 [当前]
• 1.10 AZPR SoC 整体连接
• 1.11 AZPR SoC 的仿真
• 1.12 本章总结