网志

VHDL教程-一个实际示例-第2部分-VHDL编码

吉恩·布雷尼曼 2011年5月27日

快速链接

在本系列的第1部分中,我们着重于硬件设计,包括CPLD部分I / O特性的一些VHDL定义。 在第2部分中,我们将描述 此设计的CPLD的VHDL逻辑。

对于任何设计,第一步都是收集手头工作的要求。 在本文的第1部分中,我复制了两个部分来解决CPLD设计的一些要求。

本文以PDF格式提供,便于打印

数据采集​​引擎具有 the following basic functions:

1)在高和低输出电平(受控)之间提供激光二极管的同步切换(在下面进行采样) 由一个双通道ADC,由μProcessor编程 使用SPI)的速率大约为2 KHz。

2)对于激光二极管的每个周期,收集由16个均匀间隔采样的数据包构成的数据突发,每个通道(2)每个通道16位,大约1秒钟。

3)允许各种样本长度(〜1秒,〜0.5秒等)。

4)向“μProcessor”发送“收集结束”中断信号。


该器件将被编程为用作SPI总线外设(获取运行长度和运行命令)和SPI中继器(将数据往返ADC和SRAM传递)。 该器件还将生成所有必要的时钟和控制线(SPI和I2S)以运行采集脉冲。

同样在第1部分中,我们可以将CPLD设备的设备I / O描述的片段组装成一个完整的实体。

两条CS线用于为CPLD处理的通信解码选定的SPI目标。 与设计相关的三个目标;内部外设ADC和nvSRAM(注意:两个外部目标ADC和nvSRAM的定义是CS(1)为零,这将简化解码芯片选择和SPI实现时的某些逻辑)。 最终的CS行组合(“ 11”)代表“不选择”情况。 当总体设计中的其他设备成为SPI事务的目标时,将使用此功能。对这些行进行了以下分配:

通过描述了采集引擎逻辑的主要方面,我们可以形成逻辑框图来协助冲洗功能区域的细节。

现在,我们需要查看SPI和I2S总线的时序,以便更好地了解这些总线的VHDL实现。首先,我们将研究SPI时序。 ADC和nvSRAM都使用相同的基本时序(ADC没有回读,因此SO始终处于三态)。 两种器件都在SCK的上升沿对SI线进行采样。 nvSRAM具有准备在SCK的上升沿采样的输出数据(SO)。 为了简化设计,CPLD的内部SPI外设将以相同的时序进行编码。 内部SPI外设将利用SO输出来发回采集引擎的运行状态。 所有八个状态位将被设置为运行信号的当前状态。

 

内部SPI外设命令集将由一个寄存器组成,其中包含一个运行标志和一个运行长度变量。 运行长度变量(CycleCnt)用于设置采集引擎运行的持续时间。 内部变量配置为11位,允许1到2048个激光周期的范围。 通过SPI寄存器只能设置此变量的高7位,允许运行长度为16(0x00)到2048(0x7F),以16的倍数表示。

采集引擎的一部分功能是在数据突发过程中将数据从ADC(以I2S格式)引导到nvSRAM(以SPI格式)。 为此,需要将nvSRAM配置为在开始数据突发之前接收数据。 CY14B101Q2数据手册描述了“突发模式写入”,该序列是向SRAM中连续地址写入数据的一种方式,这听起来像是此设计的正确模式。 该过程涉及选择器件(CS设置为低电平),发送WRITE指令,起始地址和多个数据字节,同时保持CS线为低电平。 WRITE指令为“ 0000 0010b”,起始地址为17位,前导零为7位,总共24位。 随后是连续的数据流,该数据流从ADC读取。 为简单起见,我们将使用起始地址零,因此我们的设置流将变为6-'0',然后是单个'1',然后是另外25个'0'(总共32位)。 在开始数据收集周期之前,应确保将详细信息(例如确保已启用设备的写操作(WREN指令))留给μProcessor处理,因为这些操作将在芯片选择序列之外继续存在(并在处理过程中通过μProcessor的直通模式将大大降低CPLD设计的复杂性。

指定了SPI操作后,让我们继续进行I2S总线。 PCM1870使用I2S总线将转换后的数据发送到设备之外。 该总线有几种不同的模式,它们决定了各种时钟的来源以及DOUT引脚上输出数据的格式。 对于这种设计,最简单的方法是让CPLD生成所有时钟信号。 因此,本设计将DSP格式用于I2S总线(LRCK和BCK是ADC的输入)。 DSP模式的设置以及ADC设置的所有其他方面将留给μProcessor处理,以便在开始数据收集周期之前进行处理。  CPLD在数据收集周期内所需要做的就是生成LRCK和BCK信号,并将来自DOUT线的信息路由到SPI总线,以将数据写入nvSRAM。

LRCK和BCK信号的时序将控制ADC的采样率。 LRCK信号启动ADC转换。 对于此设计,采样率将为31.25 KHz,该频率基于主时钟(8 MHz)除以256。 在BCK的接通周期中,LRCK信号需要为高电平,以31.25 KHz的频率重复。 在此设计中,BCK信号设置为以64倍采样率或2 MHz运行。 LRCK和BCK都需要同步到主时钟的上升沿(8 MHz)。

DSP格式的I2S时序

要将数据移出ADC并移至nvSRAM中,所需的是与I2S BCK同步的SPI SCLK信号,并将ADC DOUT信号路由至SPI SO(CPLD SO连接至nvSRAM SI线)。 还需要对SPI SCLK进行门控,以便仅在写周期内生成32个脉冲(无需在64个周期帧的其他32个周期内用无意义的数据填充nvSRAM。 进行必要的更改,我们拥有从ADC到nvSRAM的波形。 (请注意,从nvSRAM配置脉冲串到ADC数据脉冲串,整个数据采集脉冲串中nvSRAM CS信号均保持低电平)。

用于DSP格式和nvSRAM写入的I2S / SPI时序

 

了解了设计各部分之间的接口后,下一步就是创建状态图。 状态图描述了各种状态,状态内的动作以及状态之间的转换。

采集引擎状态图

好的,看起来所有逻辑都定义良好,并且我们对数据的去向有很好的了解。 因此,现在该完成VHDL编码的实现了。 在解决这个问题时,一个似乎始终显示给我的主题是,很多时机似乎都与二的幂相关。 主时钟为8 MHz,ADC BCK为2 MHz,ADC LRCK为BCK的1/64或31.25 KHz,LaserHiLow信号为ADC LRCK(1.935 KHz)的1/16,依此类推。 我认为两种模式的这些力量需要简单的计数器, 它可以被其他信号掩盖,以创建驱动该设计的必要信号。 我设想了一种基于两个计数器的设计,一个用于驱动单个激光周期(seqCount),另一个用于运行激光周期的倍数(CycleCnt)。 每个计数器的范围是0到2047,或每个11位。 seqCount的基本速率为4 MHz(或主时钟/ 2)。

有了这些信息,我们就可以解决设计中所需的必要信号。  Shown below 是内部信号,将提供设计的某些基本逻辑。第53和65行定义为两个主要计数器,而第55行定义将驱动seqcount计数器的4 MHz时钟信号。 Eng_State是我们的状态变量,在第56行与定义每个单独状态的常量声明(第57至64行)一起定义。 第69行至第71行定义了将用于提供SPI外设锁存器的内部变量(控制Run和 CycleCnt计数器的高位)。 信号NewInput是一个将在我们的主状态机中采样的标志,它宣布新命令字的到来。 运行信号(第73行)是开始数据采集周期的信号,允许状态机从IDLE步骤中移出。

其余的75至78行是两个芯片选择线各种状态的常量声明,用于为数据采集引擎中的所有SPI事务选择目标。 常量“ DE_SELECT”或“ 11”用于允许其他设备使用SPI总线,并将用于将CPLD,ADC和nvSRAM器件与SPI总线隔离。

 

现在我们可以进行逻辑设计了。 第一步是将主时钟信号除以2。 我们可以将其作为seqcount计数器上的额外长度或作为单独的除法器来处理。  由于设计中没有信号要求低至4 MHz的时序分辨率,因此我采用了单独的计数器方法(这两种选择都会导致相同的CPLD寄存器要求,尽管单独的分频器方法可能会简化可读性,并且其中的一些反测试条件) . 以下是此计数器的VHDL代码。

此过程的代码非常简单。 如果器件处于复位状态(线90,Rst ='0'),则输出信号(clkdiv2)保持逻辑低电平。  如果未声明复位,则在Mclk信号的每个下降沿,输出信号都将被触发(时钟切换为相反的逻辑状态)。 这产生的输出时钟信号是输入时钟频率的一半(8 MHz输入,4 MHz输出)。

下一步是SPI外设锁存器的实现。 该锁存器将从μProcessor接收一个8位命令,将该值保存在一个临时寄存器中,并向主状态机发出输入命令的信号。 以下是此SPI锁存器的VHDL代码。

该过程的代码稍微复杂一点,但仍然很容易掌握。 如果未寻址SPI锁存器(第106行,CS / = CPLD_SELECT,“ / =”读为“不等于”),则将SPI位计数器(SPIbitCnt)初始化为“ 111”(或7),并输入新输入标志(NewInput)被清除(第107行& 108). 如果对SPI锁存器进行了寻址,则在Sclk信号(SPI时钟)的每个下降沿,SDI输入都将移入临时寄存器的LSB(第110行)。 第111行至第115行用于对SPI位计数器进行递减计数(如果不为零),并且如果SPI位计数器为零则用于升高NewInput标志。

该过程的下一步是对状态机进行编码,该状态机将处理ADC数据的收集以及将数据路由到nvSRAM。 由于本节稍微复杂一点,让我们对其进行分解。 首先,让我们看一下主状态机的复位和时钟方面。

亮重置时,状态设置为IDLE。 此外,Laser输出被设置为低功耗,ADC左/右时钟被初始化,内部SPI时钟被抑制,完成的中断被清除并且seqcount被清除。 只要保持复位,就保持初始化。 复位释放后,mclk / 2信号的每个下降沿,序列时钟加1,并执行Eng_State情况。 这将为Eng_State状态机建立主要定时事件。 现在,让我们分解一下Eng_State案例中的每个状态。

第一状态是空闲状态。 在该状态下,将清除Int输出,以结束在突发末尾开始的中断脉冲。 第141至144行处理来自SPI锁存器的新数据。 如果已抛出NewIput标志,则此状态将新的SPI命令从临时寄存器(InputReg)复制到CycleCnt变量的Run位和高位。 如果设置了运行标志,我们通过将Eng_State推进到OUTPUT_SRAM_HEADER状态来启动突发处理,并启用内部SPI时钟(通过Eng_Sck_En)

OUTPUT_SRAM_HEADER状态仅保持状态,直到seqcount达到计数“ 1000000”。 一旦达到计数,内部SPI时钟将被禁用,seqcount会被重置,Eng_State会进入SET_LRCK_HIGH状态。 所有SPI时钟和数据脉冲均由顺序逻辑(如下所示)在下面创建。

在声明ADC左/右信号之前,SET_LRCK_HIGH状态等待seqcount升至“ 0000001”(屏蔽高位)。 此状态还管理激光二极管的高/低电流设置(seqcount的位10),并使Eng_State进入下一个状态。 在新脉冲串中的第一步或连续进行的脉冲串的继续(在同一组完整的激光二极管周期的16个样本内)进入此状态。

实际上,CLEAR_LRCK_HIGH状态的标签错误,因为在实际清除ADC左/右信号之前,该状态仅提供4 MHz的时钟延迟。 此状态所做的全部工作就是将Eng_State推进到下一个状态。

GET_CHANNEL_DATA状态清除ADC左/右信号,使能内部SPI Sck信号的产生,并将Eng_State推进到下一个状态。 这完全调节了逻辑,以将时钟数据从ADC传输到nvSRAM。 所有ADC和SPI时钟及数据脉冲均通过顺序逻辑(如下所示)在下面创建。

WAIT_FOR_SAMPLE状态仅保持状态,直到seqcount达到计数“ 1000011”。 一旦达到计数,内部SPI时钟将被禁用,并且Eng_State进入CHECK_SAMPLE_COUNT状态。 这个时期代表收集和保存 32位数据(从ADC到nvSRAM)。

CHECK_SAMPLE_COUNT状态计算给定激光二极管周期的ADC采样数。 通过测试顺序计数的第10到7位,可以确定激光周期是否已完成(“ 1111”)。 如果完成了循环,状态机将前进以确定是否已运行了请求的激光循环数(CHECK_FOR_IDLE)。 如果激光周期未完成,则状态机循环返回以开始下一个ADC采样(SET_LRCK_HIGH)。

CHECK_FOR_IDLE状态确定CycleCnt是否已减为零,即完成。 如果是这样,则清除运行标志,将中断 引发并将状态机发送回IDLE状态。 如果CycleCnt不为零,则将其递减,并将状态机设置回开始下一个激光周期(SET_LRCK_HIGH)。

完成AcqEngseq处理后,剩下的只是顺序逻辑,该逻辑完成了一些输出信号的生成。 如前所述,Eng_Sck_En信号用于使能内部SPI Sck信号。 线181显示当'Run'为'1'时,内部SPI Sck信号(ENG_Sck)是seqcount(2 MHz)LSB和Eng_Sck_En信号的逻辑与。 否则,Eng_Sck输出将通过外部SPI Sck输入(Sclk)。

内部SPI SO信号(ENG_SO)用于将数据发送到nvSRAM SI线。 驱动该信号时有三个不同的时间间隔。 首先,在OUTPUT_SRAM_HEADER状态期间,根据seqcount的当前值将该线驱动为高电平或低电平。 这段时间的大部分时间将该值驱动为低电平,除非seqcount的低6位等于“ 001011”或“ 001100”(第182-185行)。 在nvSRAM写前导的第7位的数据窗口期间,这会在nvSRAM SI线上产生一个高电平正脉冲。 否则,在“运行”间隔的其余时间内,将使用ADC_Data输入的当前值来驱动ENG_SO,或者在所有其他时间,通过外部SPI SDI输入(行186)来驱动ENG_SO。

第187和188行显示了nvSRAM_CS和ADC_CS的逻辑。 当“运行”处于活动状态或CS线设置为“ 01”时,将选择nvSRAM。 ADC_CS仅由μProcessor使用,并且在“运行”为低电平且CS线设置为“ 00”时被驱动为低电平(有效)。

外部SPI SDO输出被编码为要么穿过内部SPI SI线的同时选择NVSRAM的是(用于读取所捕获的数据),或状态读出的“运行”标志,而CS线是等于“ 10英寸(第190行)。否则,该线路为三态,以便其他SPI器件可以驱动该线路(选择时)。

剩下的语句(第191行)使ADC_BCK信号以seqcount(2 MHz)的LSB驱动。 

ew,我认为我已经(终于)到达了一个不错的停留地点。 VHDL代码已经完成,但是我们应该确保它能够达到我们的预期。 让我们下次再说。 在本文的第三部分中,我将介绍完整的VHDL代码(主要在这里,只是分解为可消化的部分),以及测试台和一些时序跟踪。

到后来,

基因  



要发布对评论的回复,请单击每个评论所附的“回复”按钮。要发布新评论(而不是回复评论),请查看评论顶部的“写评论”标签。

注册后,您可以参加所有相关网站上的论坛,并获得所有pdf下载的访问权限。

注册

我同意 使用条款 隐私政策 .

试试我们偶尔但很受欢迎的时事通讯。非常容易退订。
或登录