spinal HDL - 09 - 时序逻辑

简介: spinal HDL - 09 - 时序逻辑

Registers


用 SpinalHDL 创建寄存器与用 VHDL 或 Verilog 创建寄存器非常不同。在SpinalHDL ,没有过程/总是阻塞。寄存器在声明中明确定义。这与传统的事件驱动 HDL 的区别有很大的影响:

  • 您可以在相同的范围内分配寄存器和连接,这意味着代码不需要在进程/总是块之间分割。
  • 它使一些事情更加灵活(参见函数),并且时钟和重置是分开处理的。

实例化


有4种方法可以实例化一个寄存器:

image.png

下面是一个声明某些寄存器的例子:

// UInt register of 4 bits
val reg1 = Reg(UInt(4 bit))
// Register that samples reg1 each cycle
val reg2 = RegNext(reg1 + 1)
// UInt register of 4 bits initialized with 0 when the reset occurs
val reg3 = RegInit(U"0000")
reg3 := reg2
when(reg2 === 5) {
  reg3 := 0xF
}
// Register that samples reg3 when cond is True
val reg4 = RegNextWhen(reg3, cond)

上面的代码将推断出以下逻辑:

image.png

上面的 reg3示例显示了如何分配 RegInit 寄存器的值。也可以使用相同的语法分配给其他寄存器类型(Reg、 RegNext、 RegNextWhen)。就像组合赋值一样,规则是“最后一次赋值获胜”,但是如果没有完成赋值,寄存器保留其值。

RegNext 是一个基于 Reg 语法构建的抽象。下面两个代码序列是严格等价的:

// Standard way
val something = Bool()
val value = Reg(Bool())
value := something
// Short way
val something = Bool()
val value = RegNext(something)

Reset value 重置值


除了使用 RegInit (value: Data)语法直接创建具有重置值的寄存器之外,还可以通过调用寄存器上的 init (value: Data)函数来设置重置值。

// UInt register of 4 bits initialized with 0 when the reset occurs
val reg1 = Reg(UInt(4 bit)) init(0)

如果有一个包含 Bundle 的寄存器,则可以对 Bundle 的每个元素使用 init 函数。

case class ValidRGB() extends Bundle{
  val valid   = Bool()
  val r, g, b = UInt(8 bits)
}
val reg = Reg(ValidRGB())
reg.valid init(False)  // Only the valid if that register bundle will have a reset value.

用于仿真的初始化值


对于在 RTL 中不需要重置值,但是需要初始化值进行模拟(以避免 x 传播)的寄存器,可以通过调用 randBoot ()函数来请求一个随机初始化值。

// UInt register of 4 bits initialized with a random value
val reg1 = Reg(UInt(4 bit)) randBoot()

RAM/ROM


语法


要用 SpinalHDL 创建内存,应该使用 Mem 类。它允许您定义一个内存并向其添加读写端口。

下表显示了如何实例化一个内存:

image.png

如果你想定义一个 ROM,initialContent 数组的元素应该只是文字值(没有运算符,没有调整大小的函数)。这里有一个例子。要给出 RAM 初始值,还可以使用 init 函数。

下表显示了如何在内存中添加访问端口:

image.png

如果出于某种原因,你需要一个在 Spinal 中没有实现的特定内存端口,你总是可以通过为它指定一个 BlackBox 来抽象你的内存。SpinalHDL 中的内存端口不是推导出来的,而是显式定义的。不应该像 VHDL/Verilog 中那样使用编码模板来帮助合成工具推断内存。

下面是一个简单的双端口 ram (32位 * 256)的例子:

val mem = Mem(Bits(32 bits), wordCount = 256)
mem.write(
  enable  = io.writeValid,
  address = io.writeAddress,
  data    = io.writeData
)
io.readData := mem.readSync(
  enable  = io.readValid,
  address = io.readAddress
)

读写策略


此策略指定在同一循环中对同一地址进行写操作时,读操作如何受到影响。

image.png

生成的 VHDL/Verilog 始终处于 readFirst 模式,该模式与 dontCare 兼容,但与 writeFirst 不兼容。要生成包含这种特性的设计,您需要启用自动内存黑盒。

Mixed-width ram


你可以使用以下函数指定访问内存的端口,其宽度为内存宽度的两分之一的幂:

image.png

至于读写策略,要使用这个特性,您需要启用自动内存黑盒,因为没有通用的 VHDL/Verilog 语言模板来推断混合宽度内存。

Automatic blackboxing


由于不可能用常规的 VHDL/Verilog 推断所有的 ram 类型,SpinalHDL 集成了一个可选的自动黑箱系统。这个系统查看你的 RTL 网络列表中的所有内存,并用黑匣子替换它们。然后,生成的代码将依靠第三方 IP 提供内存特性,如写入期间读取策略和混合宽度端口。

下面是一个默认情况下如何启用存储器黑盒的例子:

def main(args: Array[String]) {
  SpinalConfig()
    .addStandardMemBlackboxing(blackboxAll)
    .generateVhdl(new TopLevel)
}

Blackboxing policy


你可以使用多种策略来选择你想要黑匣子的内存,以及当黑匣子不可行时该怎么做:

image.png

要显式地将内存设置为黑盒,可以使用其 generateAsBlackBox 函数。

val mem = Mem(Rgb(rgbConfig), 1 << 16)
mem.generateAsBlackBox()

您还可以通过扩展 MemBlackboxingPolicy 类来定义自己的 blackboxing 策略。

Standard memory blackboxes


下面是 SpinalHDL 中使用的标准黑盒的 VHDL 定义:

-- Simple asynchronous dual port (1 write port, 1 read port)
component Ram_1w_1ra is
  generic(
    wordCount : integer;
    wordWidth : integer;
    technology : string;
    readUnderWrite : string;
    wrAddressWidth : integer;
    wrDataWidth : integer;
    wrMaskWidth : integer;
    wrMaskEnable : boolean;
    rdAddressWidth : integer;
    rdDataWidth : integer
  );
  port(
    clk : in std_logic;
    wr_en : in std_logic;
    wr_mask : in std_logic_vector;
    wr_addr : in unsigned;
    wr_data : in std_logic_vector;
    rd_addr : in unsigned;
    rd_data : out std_logic_vector
  );
end component;
-- Simple synchronous dual port (1 write port, 1 read port)
component Ram_1w_1rs is
  generic(
    wordCount : integer;
    wordWidth : integer;
    clockCrossing : boolean;
    technology : string;
    readUnderWrite : string;
    wrAddressWidth : integer;
    wrDataWidth : integer;
    wrMaskWidth : integer;
    wrMaskEnable : boolean;
    rdAddressWidth : integer;
    rdDataWidth : integer;
    rdEnEnable : boolean
  );
  port(
    wr_clk : in std_logic;
    wr_en : in std_logic;
    wr_mask : in std_logic_vector;
    wr_addr : in unsigned;
    wr_data : in std_logic_vector;
    rd_clk : in std_logic;
    rd_en : in std_logic;
    rd_addr : in unsigned;
    rd_data : out std_logic_vector
  );
end component;
-- Single port (1 readWrite port)
component Ram_1wrs is
  generic(
    wordCount : integer;
    wordWidth : integer;
    readUnderWrite : string;
    technology : string
  );
  port(
    clk : in std_logic;
    en : in std_logic;
    wr : in std_logic;
    addr : in unsigned;
    wrData : in std_logic_vector;
    rdData : out std_logic_vector
  );
end component;
--True dual port (2 readWrite port)
component Ram_2wrs is
  generic(
    wordCount : integer;
    wordWidth : integer;
    clockCrossing : boolean;
    technology : string;
    portA_readUnderWrite : string;
    portA_addressWidth : integer;
    portA_dataWidth : integer;
    portA_maskWidth : integer;
    portA_maskEnable : boolean;
    portB_readUnderWrite : string;
    portB_addressWidth : integer;
    portB_dataWidth : integer;
    portB_maskWidth : integer;
    portB_maskEnable : boolean
  );
  port(
    portA_clk : in std_logic;
    portA_en : in std_logic;
    portA_wr : in std_logic;
    portA_mask : in std_logic_vector;
    portA_addr : in unsigned;
    portA_wrData : in std_logic_vector;
    portA_rdData : out std_logic_vector;
    portB_clk : in std_logic;
    portB_en : in std_logic;
    portB_wr : in std_logic;
    portB_mask : in std_logic_vector;
    portB_addr : in unsigned;
    portB_wrData : in std_logic_vector;
    portB_rdData : out std_logic_vector
  );
end component;

如你所见,blackboxes 有一个技术参数。要设置它,可以在相应的内存上使用 setTechnology 函数。目前有四种可能的技术:

  • auto
  • ramBlock
  • distributedLut
  • registerFile
目录
相关文章
|
存储 数据采集
时序逻辑电路的应用及其作用
一、什么时序逻辑电路 时序逻辑电路是一种电子电路,用于处理和存储时序信息。它通过使用时钟信号来控制电路的行为,以实现特定的功能。 时序逻辑电路通常由触发器和组合逻辑电路组成。触发器是一种存储器件,可以存储和传递电信号。组合逻辑电路则根据输入信号的组合产生输出信号。 时序逻辑电路的行为是根据时钟信号的变化来确定的。时钟信号是一个周期性的信号,用于同步电路的操作。在每个时钟周期中,电路根据输入信号和当前状态来计算输出信号,并在时钟信号的上升沿或下降沿时更新状态。 时序逻辑电路可以用于实现各种功能,如计数器、状态机、时序控制器等。它在数字系统中起着重要的作用,用于处理时序信息和控制电路的行为。 二、
739 0
|
5月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的MSK调制解调系统verilog开发,包含testbench,同步模块,高斯信道模拟模块,误码率统计模块
升级版FPGA MSK调制解调系统集成AWGN信道模型,支持在Vivado 2019.2中设置不同SNR仿真误码率。示例SNR值从0到15,结果展示解调质量随SNR提升。MATLAB仿真验证了MSK性能,图片显示了仿真结果。 ### 理论概要 研究聚焦于软件无线电中的MSK调制解调,利用Verilog实现。MSK是一种相位连续、恒包络的二进制调制技术,优点包括频谱效率高。系统采用无核设计,关键模块包括调制器、解调器和误码检测。复位、输入数据、中频信号等关键信号通过Verilog描述,并通过Chipscope在线观察。
115 6
基于FPGA的MSK调制解调系统verilog开发,包含testbench,同步模块,高斯信道模拟模块,误码率统计模块
|
芯片 异构计算
第三章 硬件描述语言verilog(三)功能描述-时序逻辑
第三章 硬件描述语言verilog(三)功能描述-时序逻辑
279 0
第三章 硬件描述语言verilog(三)功能描述-时序逻辑
|
存储 缓存 算法
m基于FPGA的交织解交织系统verilog实现,包含testbench
m基于FPGA的交织解交织系统verilog实现,包含testbench
316 0
|
算法 异构计算
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
166 0
|
算法 异构计算
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
322 0
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
|
算法 异构计算
Verilog HDL数据流建模与运算符
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
214 0
Verilog HDL数据流建模与运算符
|
算法 异构计算
Verilog HDL行为级建模
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
115 0
Verilog HDL行为级建模
|
Scala
spinal HDL - 05 - Spinal HDL - 函数和时钟域
spinal HDL - 05 - Spinal HDL - 函数和时钟域
303 0
spinal HDL - 05 - Spinal HDL - 函数和时钟域
Verilog 时序控制
Verilog 提供了 2 大类时序控制方法:时延控制和事件控制。事件控制主要分为边沿触发事件控制与电平敏感事件控制。
156 0