3 系统现实 设计一个软核,首先要确定本核与外部的信息交换通路,即本模块与外部电路的接口,友好的接口模型可以大大提高ip核的使用范围。图1是串行口ip核的接口模型图,图中左边的引脚是输入引脚,右边是输出引脚,图中引脚的宽度均有标明(未写出宽度表明宽度为“1”)。由于本设计不支持i/o口的复用,所以为串行口工作于模式0下增加了rxd_o和rxdwr两个输出引脚,前者用于输出,后者为输出有效控制位(rxdwr为高电平时,指示输出)。串行口控制寄存器scon在设计中也被分成两部分,一部分接收来自控制器的输入(一共6位,sm0、sm1、sm2、ren、tb8和ri),一部分作为输出送往控制器(一共3位,rb8以及分别指示发送和接收完毕的两位,控制器会根据这两位来对中断标志置位)。至于发送中断标志位ti,由于其不会影响串行口的工作过程所以在此并未列出,它将在控制器中得到体现。 图1 串行口输入输出引脚图 串行口有四种工作方式,方式0的波特率固定的(为1/12倍的输入时钟频率);方式1和方式3的波特率是定时器/计数器1的溢出率的1/16或1/32倍;而方式2的波特率则是输入时钟频率的1/32或1/64倍。由此可见,首先必须要解决的问题就是设计分频器。由于在方式0下,串行口是作半双工的同步移位寄存器使用,其发送和接收的波特率是一样的,因此对于方式0,串行口内部的分频信号只需要一路即可。但是对于其他方式,串行口均是工作在全双工的状态下,因此,每种方式下均需要两路分频信号,一个用于发送,一个用于接收。 对于方式0和方式2,其分频的对象是输入时钟,实现这种分频器的主要vhdl代码的如下:(信号s_count_enable的频率为输入时钟频率的1/12,其高电平维持时间与一个输入时钟的周期相等。) s_count_enable<=`1`when s_pre_count=conv_unsigned(11,4) else `0`; p_divide_clk: process (clk, reset) begin if reset = `1` then s_pre_count <= conv_unsigned(0,4); else if clk`event and clk=`1` then if s_pre_count = conv_unsigned(11,4) then s_pre_count <= conv_unsigned(0,4); else s_pre_count <= s_pre_count + conv_unsigned(1,1); end if; end if; end if; end process p_divide_clk; 对于方式1和方式3,其分频的对象都是定时器/计数器1的溢出率,且分频的系数是一样的。由于串行口只有一个,它不可能调试工作在方式1和3,因此方式1和方式3可以共用分频信号。要对定时器/计数器1的溢出率进行分频,首先必须测出其频率,为此需要一个上升沿的检测器来监测外部输入的定时器/计数器1的溢出信号(图1中的tf_i引脚),这样的检测器可以双稳态触发器来实现,限于篇幅,双稳态触发器的vhdl实现请参考文献[2],这里就不加详述。检测器在监测到tf_i引脚的上升沿时就使其输出信号保持一个时钟周期的高电平,再另外设计计数器对此高电平进行计数,以达到对其分频的效果。 除了作为同步移位寄存器的方式0外,其他作uart(通用异步接收和发送器)用的三种方式在接收外部输入的时候均需要对外部的输入信号采样监测以确定信号的值。因此一个位检测器是必需的。同时,串行口工作在方式1、方式2和方式3时,每一个接收的数据帧都有一个起始位,这个起始位被固定为0,也就是说在输入端rxd_i监测到1到0的跳变就会启动接收过程(注:负跳变检测器对外部输入引脚的采样频率为波特率的16倍)。负跳变检测器和上述上升沿检测器的设计思路是一样的。位检测器的设计原理是把一个接收位的时间分为16等分(以内部计数器的16个状态来表示),在计数器的7、8、9状态时,位检测器对外部输入端的值进行采样。采用3取2的表决方法来抑制噪声。如果位检测器检测到接收的第一位不是0,那么就说明它不是一帧数据的起始位,应该摈弃,接收电路复位。 至于接收器和发送器,这里采用有限状态机的方式来实现。由于要工作在全双工模式下,所以接收和发送要采用两个有限状态机。有限状态机的每一个状态用来发送/接收移位数据。发送和接收的具体实现方式是移位。移位的时钟来自上面所述的分频器的,也就是根据各自的波特率进行。虽然此部分很繁琐,但是实现并不困难,请读者参阅文献[3]、[4]和[5]。 |