利用AHB-Lite总线实现ARM Cortex-M0基础的SoC系统;如何设计一个SoC系统;AHB-Lite;ARM Cortex-M0;SoC;

news2024/12/24 19:42:50

利用AHB-Lite总线实现ARM Cortex-M0基础的SoC系统;如何设计一个SoC系统

  • 一、SoC系统下的软硬件分工
    • Hardware only 和System on Chip的区别
      • 例子1:计算时间差值
      • 例子2:想实现功能的切换
      • 例子3:LED显示
  • 二、SoC系统
    • 1. AHB-Lite总线 与 ARM Cortex-M0
    • 2. 软硬件层面通过总线实现握手
    • 3. Memory Mapped I/O(内存映射 I/O)
      • I/O device
    • 4. SoC和microcontroller的区别
  • 三、软件层面C语言代码
    • 1. C指针对分配地址定义——外设I/O寄存器的地址映射
    • 2. 读取I/O寄存器(Read)
    • 3. 写入I/O寄存器(Write)
  • 四、硬件层面SystemVerilog代码
    • 0. AHB-Lite传输协议
    • 1. 片选信号HSEL_SIGNALS
    • 2. 地址信号HADDR
      • 控制信号write_enable 和 read_enable 和 word_address
    • 3. 读数据HRDATA
      • 子模块利用word_address产生HRDATA
      • interconnect里对信号进行选择
      • 子模块中 status 寄存器
    • 4. 写入数据HWDATA
    • 5. 读写控制HWRITE
    • 6. HREADY
    • 7. 数据有效信号:DataValid和NextDataValid用于减少由于异步更新带来的可能问题
  • 五、后记

  本文讨论了SoC系统的架构设计,包括处理器核心、内存以及其他外设的互连,并详细描述了如何通过AHB-Lite总线实现高效的数据传输。AHB-Lite总线是一种简化版本的AHB总线。同时,阐述了利用寄存器映射以及其他硬件资源与软件接口的设计方法,以满足SoC系统的功能需求。本文为未来更复杂的SoC设计提供参考。

一、SoC系统下的软硬件分工

  在设计一款SOC时,首先要考虑清楚的是“软硬件分工”。我们团队在做项目的时候犯过错误,因为硬件工程师惯性思维想到所有的功能其实都可以在硬件中完成,只是单纯的想把处理这种功能的模块“放置”在总线上,这是不正确的。(”TOTALLY IN WRONG DIRECTION“——Iain)。

  有的功能只有硬件能实现,比如精准地计数、定时(利用时钟周期)、存储、按键消抖、同步器。

  有些情况下编写C语言代码可以更方便,比如算法(如滤波函数)、计算(如果用硬件做除法会极其麻烦)、控制(如传感器、OLED显示)。大多数时候处理器核心是封装好的、混淆源代码后的CPU(像ARM核这种第三方IP就不允许修改核心内部),它可以一条一条地执行指令,C语言代码经过编译之后得到机器指令,可以让CPU完成算法或者控制。

Hardware only 和System on Chip的区别

  SoC比纯硬件layout整体做下来layout面积要大不少(主要是处理器核占的比较大,还有debounce也占的大),但是软件层面的子函数切换调用显然比硬件中很多很多状态的状态机切换要舒服。

  • 硬件设计时还需要考虑设计SoC子模块中的IO寄存器有哪些;
  • SoC要考虑清楚软硬件协同工作!

例子1:计算时间差值

  举个例子:比如判断开关是否被按下这个动作,并且计算两次按下开关之间的时间。

  • 在软件层面,如果把按下开关的数据放在寄存器里,并在C语言里调用读取,可以得到开关的0和1或者是次数差值,如果想计算时间,需要在C语言里调用现在的时间计数器,将时间计数的差值算出来。而一条指令的执行往往需要多个时钟周期,在需要计算时间差值时实际上已经产生了不小的误差,甚至可能会错过。

  • 而在硬件层面,在always_ff里写判断到开关被按下,记录现在的时间计数值或两次按下的差值,就可以得到非常精准的时间差,而C语言可以调用这个时间差并进行后续运算。

例子2:想实现功能的切换

再比如:想实现功能的切换。

  • 在硬件层面,可以使用状态机进行状态跳转,在下一状态赋值。非常不灵活,改代码也要改很多。

  • 在软件层面,C语言是在while(1)循环里直接if判断,成立就调用子函数。CPU会自动编译成跳转指令,跳转过去执行其他指令。

例子3:LED显示

再比如:LED显示。

  • 在软件层面,如果利用ndigit[3:0]给4个LED进行片选,让每个LED点亮。实际上在LED点亮之前可能会执行其他的运算,会使得4个LED的点亮周期不一样,在硬件上显得有些LED会更亮有些会比较暗。

  • 在硬件层面,LED的写入以周期性规律地写入到寄存器,会让每个LED的片选点亮周期一致。

  好的软硬件配合是:硬件层面LED规律地点亮,软件层面负责将需要点亮的值的结果发到底层硬件,可以根据软件写下来地值平衡地输出到LED上。“Doing something every certain number of clock cycles is really really really easy in hardware, but it’s difficult to do in software.”

二、SoC系统

  为了实现所需功能的需求,需要在处理器核心外部挂载设备或功能模块,使用总线通信的方式是很好的思路。

1. AHB-Lite总线 与 ARM Cortex-M0

  AHB-Lite总线:AHB(AMBA High-performance Bus)是AMBA协议的一部分,用于连接高性能的系统模块。AHB-Lite是AHB的一个简化版本,设计用于低成本和低功耗的系统,同时保持了AHB的关键特性。AHB-Lite的主要特性包括:

  1. 单周期非阻塞读取或写入
  2. 支持突发(burst)传输
  3. 只有一个主设备(master)和一个或多个从设备(slave)
  4. 握手协议确保数据传输的正确性

  ARM Cortex-M0处理器:是ARM Holdings的一个微处理器核心,采用ARMv6-M指令集,设计用于低成本和低功耗的应用。Cortex-M0的主要特性包括:

  1. 32位RISC(Reduced Instruction Set Computer)架构。
  2. 支持Thumb-1指令集,它比原始的ARM指令集更节省存储空间。
  3. 使用von Neumann架构,数据和指令共享同一总线和内存。
  4. 内置了Nested Vectored Interrupt Controller(NVIC),支持多达32个中断源。

在这里插入图片描述

  Cortex-M0与AHB-Lite总线之间的连接通常通过一个总线接口模块进行,这个模块会将处理器的读取和写入请求转化为AHB-Lite协议的操作。在AHB-Lite总线协议中,只支持单周期突发。

2. 软硬件层面通过总线实现握手

  在软硬件系统的角度考虑整个SoC系统,要想从软件层面实现一定的功能比如读、写。握手通常是通过一系列的信号交换来实现硬件和软件之间的同步。握手的基本目标是确保数据在发送者和接收者之间正确地传输。

  • c语言在子模块的寄存器时,要先向读status寄存器,检查是否可以读数,如果可读就返回1,这样就可以执行读取该寄存器的功能;
  • c语言在子模块的寄存器时,要先向下写一个1到status寄存器,意味着请求写入,然后可以往指定寄存器写入想写入的值;

3. Memory Mapped I/O(内存映射 I/O)

  在SoC(系统片上芯片)设计中,Memory Mapped I/O(内存映射I/O)是一种常见的技术,它允许使用与内存相同的地址空间来访问I/O设备。它的基本思想是将外设寄存器映射到特定的内存地址上,使得处理器可以通过读写这些内存地址来与外设进行通信。IO device 寄存器被内存映射分配到唯一的一个地址上,软件层面C语言代码利用指针即可对指定地址进行读写操作。

在这里插入图片描述

  • 从软件层面考虑,Memory Mapped I/O 提供了一种简单的编程模型。由于外设被映射到内存地址空间,软件可以使用与访问内存相同的指令和指令操作数来访问外设。通过读写特定的内存地址,软件可以与外设进行通信。这种统一的编程模型简化了软件开发,减少了对特定外设的编程细节的处理。

  • 从硬件层面考虑,Memory Mapped I/O 的关键在于将外设寄存器与内存地址进行映射。外设寄存器与内存控制器连接,并使用特定的地址范围进行访问。处理器通过访问这些地址来读写外设寄存器,内存控制器将读写操作路由到相应的外设。这种映射简化了硬件设计,减少了专用I/O指令或接口的需求。

SoC设计时C语言程序将每个输入/输出设备视为占用一个或多个内存位置。通过总线上地址的译码可以找到挂载的硬件设备的地址映射。

I/O device

  I/O device表现在寄存器级的描述上,这些寄存器为处理器(或其他系统组件)和I/O设备之间的通信提供了接口。处理器(或其他系统组件)通过读写这些寄存器来控制I/O设备。例如,要发送数据,处理器可以先检查状态寄存器,看看设备是否已经准备好。然后,处理器可以把数据写入数据寄存器,然后通过设置控制寄存器来启动传输。

  在考虑I/O设备的性能时为了配置寄存器分为:

  • read/write
  • read only,就是不提供write_enable的读取功能
  • write only,就是不提供read_enable的读取功能
  • no direct access at all,存访中间值,不让读写。

在这里插入图片描述

4. SoC和microcontroller的区别

  第n次被怼:iain评价我们不是在做soc而是在做microcontroller…

  我们总结分析了当时不完整设计两点原因:

  1. 在proposal画的soc图里I/O寄存器的端口不是专用的。举个例子:一个mircrocontroller不是专门用于显示LED的,但是它可以显示LED。而一个SoC是针对具体硬件的设计,作为SoC工程师你得明白你在设计什么硬件。一个mircrocontroller只有输入口和输出口,没有什么不同,而custom interface可以清楚地告诉你status寄存器在哪、数据计数器counter在哪。
  2. 没有握手信号。握手信号在SoC设计中起着关键作用,尤其是在数据传输和通信中。在你的设计中,如果C语言写的函数不能正确地从寄存器读取或写入数据,可能是因为有些硬件细节没有被正确处理。例如,可能需要检查握手信号的状态,以确保数据已经准备好,或者可能需要正确地配置总线协议和寄存器。

三、软件层面C语言代码

  寄存器被分配唯一的一个地址上,通过CPU向该地址写数据,即可对该外设进行配置,通过CPU向该地址写数据,即可得到该外设的状态。
【集创赛】基于arm处理器的SOC设计【2】

1. C指针对分配地址定义——外设I/O寄存器的地址映射

  在C语言中,利用指针即可对指定地址进行读写操作。将指针指向一个地址,向该指针写数据即可实现向分配到该地址的寄存器写入数据,读取该指针即可读取指向该地址的寄存器的值。
如下,定义一个基地址叫AHB_CUSTOM_BASE的为0x40000000:

  • volatile 表示使用变量的时候直接从内存拿,所修饰的变量是易变的;
#define AHB_CUSTOM_BASE       0x40000000
volatile uint32_t* CUSTOM_REGS = (volatile uint32_t*) AHB_CUSTOM_BASE;
//[0] 4000 0000   第一个32bits的I/O寄存器的地址,比如他是一个counter
//[1] 4000 0004   第二个32bits的I/O寄存器的地址,比如他是一个LED
//[2] 4000 0012   可能是status寄存器哦

2. 读取I/O寄存器(Read)

  • 首先需要定义一个读寄存器的函数
uint32_t read_CUSTOM_counter(void)        { return IO_REGS[0];}        
  • 定义一个bool函数,用于检查是否可以读数的函数用于握手
bool check_CUSTOM_status(int num_reg){  
    int status,ready;
    status = CUSTOM_REGS[2]; //[2]是CUSTOM这个设备中IO寄存器们中的Status寄存器
    ready = (status >> num_reg) &1;  //status, 每一bit对应着第几位的IO寄存器是否可读
    return (ready == 1);    //如果可读就返回1,这样就可以执行if语句里读取的命令啦
}
  • 在main函数中请求读取,需要先检查Status寄存器中DataValid是否可读,在总线上只有握手成功才可以读取
while(1)
{
    if (check_CUSTOM_status(0))   // 检查第一个32bits的I/O寄存器【0】是不是可以读的
        {
        counter = read_CUSTOM_counter(); 
        }
}

3. 写入I/O寄存器(Write)

  • 先得请求写入操作,也就是在总线上要拉起来Datavalid
void write_CUSTOM_LED(uint32_t value)   {
CUSTOM_REGS[2] = 1<<1;  // pull up datavaild, 将1左移1位写入status寄存器,请求写入
CUSTOM_REGS[1] = value;} //然后将值写入到【1】这个LED的IO寄存器里
  • 在主函数中调用
while(1)
{
    write_CUSTOM_LED(1); //写入32bit的0000....1,写入数值1。
}

四、硬件层面SystemVerilog代码

Iain McNally 讲义

0. AHB-Lite传输协议

  在AHB-Lite(高级高速总线Lite)协议中,存在两种数据传输类型:写传输(write transfer)和读传输(read transfer)。这些传输类型用于在AHB-Lite总线上进行数据的写入和读取操作。
在这里插入图片描述

  以下是相关信号的简要介绍:

  1. HCLK(AHB-Lite时钟信号):HCLK是AHB-Lite总线的时钟信号,用于同步数据传输和协议操作。
  2. HADDR[31:0](地址信号):HADDR是一个32位的信号,用于传输读取或写入操作的目标地址。它指定了要读取或写入的设备或内存的地址。
  3. HWRITE(写使能信号):HWRITE是一个单比特的信号,用于指示传输是否是写操作。当HWRITE为高电平时,表示当前传输是写操作;当HWRITE为低电平时,表示当前传输是读操作。
  4. HRDATA[31:0](读数据信号):HRDATA是一个32位的信号,用于传输从目标设备或内存读取的数据。在读传输期间,HRDATA携带来自目标设备或内存的数据。
  5. HREADY(就绪信号):HREADY是一个单比特的信号,由目标设备或内存传递给主设备,用于指示数据传输的就绪状态。当HREADY为高电平时,表示目标设备或内存已准备好进行下一次传输;当HREADY为低电平时,表示目标设备或内存暂时不可用,主设备应等待。

在这里插入图片描述

  • 在写传输中,主设备通过将目标地址和数据写入对应的信号(HADDR和HRDATA)来执行写入操作,并首先将HWRITE设置为高电平。目标设备或内存接收到写入请求后,进行相应的写入操作,并通过HREADY信号指示就绪状态。

  • 在读传输中,主设备通过将目标地址写入HADDR,并将HWRITE设置为低电平,发起读取操作。目标设备或内存根据目标地址,读取相应的数据,并将数据通过HRDATA传递给主设备。同时,目标设备或内存使用HREADY信号指示就绪状态。

这些信号在AHB-Lite协议中起着关键的作用,确保了数据传输的正确性、同步性和可靠性。

ARM® AMBA® 5 AHB 协议规范

1. 片选信号HSEL_SIGNALS

在这里插入图片描述

  要去理解总线上设备切换的原理:第一步,可以看出这根AHB-Lite总线上共挂载了4个外设:ROM,RAM,Keyboard Interface, Display Interface。这些信号由ARM Cortex-M0核每条指令执行时所输出的HADDR选择,内存映射划分的区域,如果访问的地址HADDR在这个范围之内就说明像选中的是这个子模块,为这个子模块(HSEL_SIGNALS的第几个bit)拉高。片选到了哪个子模块,哪个子模块的HREADY拉起,

  注:ARM M0核是冯诺依曼结构的,也就是他的数据和指令是在同一片存储上,即指令和数据统一寻址,取指令和取数据都通过AHB-Lite总线,这样效率不够高。相反,哈佛结构把指令通路和数据通路分开,效率很明显更高,学的picoMIPS就是哈佛架构。

  always_comb
    if ( HADDR < 32'h2000_0000 )
      HSEL_SIGNALS = 1 << 0;
    else if ( HADDR < 32'h4000_0000 )
      HSEL_SIGNALS = 1 << 1;
    else if ( HADDR < 32'h5000_0000 )
      HSEL_SIGNALS = 1 << 2;
    else if ( HADDR < 32'h6000_0000 )
      HSEL_SIGNALS = 1 << 3;
    else
      HSEL_SIGNALS = 1 << 4;

  从AHB-Lite Interconnect出来的HSEL_SIGNALS信号,第几个bit,连接到顶层对slave子模块进行片选

// ahb_interconnect.sv
  
// AHB interconnect including address decoder, register and multiplexer
  ahb_interconnect interconnect_1 (
    .HCLK, .HRESETn, .HADDR, .HRDATA, .HREADY,
    .HSEL_SIGNALS({HSEL_OPORT,HSEL_IPORT,HSEL_RAM,HSEL_ROM}),
    .HRDATA_SIGNALS({HRDATA_OPORT,HRDATA_IPORT,HRDATA_RAM,HRDATA_ROM}),
    .HREADYOUT_SIGNALS({HREADYOUT_OPORT,HREADYOUT_IPORT,HREADYOUT_RAM,HREADYOUT_ROM})
  );
// AHBLite Slaves   
  ahb_rom rom_1 (
    .HCLK, .HRESETn, .HADDR, .HWDATA, .HSIZE, .HTRANS, .HWRITE, .HREADY,
    .HSEL(HSEL_ROM),
    .HRDATA(HRDATA_ROM), .HREADYOUT(HREADYOUT_ROM)
  );

2. 地址信号HADDR

   HADDR 是地址总线,通常用于携带当前读取或写入操作的内存地址。其宽度由系统地址总线的宽度决定。在一个 AHB-Lite 总线事务中,主设备(Master)通过 HADDR 信号将目标地址传递给从设备(Slave)。详细见前文介绍memory mapped I/O

控制信号write_enable 和 read_enable 和 word_address

   对于一个在总线上可读可写的子模块里,要生成是否可读的信号read_enable,和是否可写的信号write_ebale。这两个信号存在于子模块的内部。同时提取HADDR的[4:2]作为子模块中IO寄存器的选择信号word_address。

  • 对于只读或只写的文件,相应地没有read_enable 和write_enable 。
在所有的子模块.sv里
//Generate the control signals in the address phase
  always_ff @(posedge HCLK, negedge HRESETn)
    if ( ! HRESETn )
      begin
        read_enable <= '0;
        word_address <= '0;
        write_enable <= '0;
      end
    else if ( HREADY && HSEL && (HTRANS != No_Transfer) )
      begin
        write_enable <= HWRITE;
        read_enable <= ! HWRITE;
        word_address <= HADDR[4:2];
     end
    else
      begin
        write_enable <= '0;
        read_enable <= '0;
        word_address <= '0;
     end

3. 读数据HRDATA

  从主模块中期待读取到子模块的信号,从slave将HRDATA发到master。

子模块利用word_address产生HRDATA

  // read
  always_comb
    if ( ! read_enable )
      // (output of zero when not enabled for read is not necessary
      //  but may help with debugging)
      HRDATA = '0;
    else
      case (word_address)
        0 : HRDATA = counter_0;//nMode
        1 : HRDATA = counter_1;//nTrip
	    2 : HRDATA = counter_2;//flag
        3 : HRDATA = Status;  
        // unused address - returns zero
        default : HRDATA = '0;
      endcase

interconnect里对信号进行选择

  从各个子模块中可以传到interconnect里,共几个子模块就有几个HRDATA,需要进行选择。

  input [num_slaves-1:0][31:0] HRDATA_SIGNALS,   

  片选信号参与选择哪个子模块发送过来的信号是HRDATA,从而输出到Contex-M0核中

always_ff @(posedge HCLK, negedge HRESETn)
    if( ! HRESETn )
      mux_sel <= '0;
    else if( HREADY )
      mux_sel <= HSEL_SIGNALS;

  always_comb
    begin
      // default values
      HREADY = 1;
      HRDATA = 32'hDEADBEEF; // "hexspeak" to indicate an error has occured
      
      // since num_slaves is a parameter all of this should be unrolled at compile time
      
      for ( i = 0; i < num_slaves; i++ )
        if ( mux_sel == (1 << i) )
          begin
            HREADY = HREADYOUT_SIGNALS[i];
            HRDATA = HRDATA_SIGNALS[i];
          end

    end

  这里需要注意的是:

  • HREADYOUT控制HRDATA的读出数据周期。高:Slave输出传输结束;低:Slave需延长传输周期。在本设计里,一个周期可以完成读出数据的操作,所以所有的HREADYOUT都置1。
  assign HREADYOUT = '1; //Single cycle Write & Read. Zero Wait state operations

子模块中 status 寄存器

  • 子模块中的status寄存器存放着DataValid信号。要想在读取时实现握手,需要先检查子模块的数据是否已经准备好,即检查DataValid。
  • 在C语言中调用check_CUSTOM_status()函数来检查这个子模块内部的status寄存器。
  // Storage for status bits 
  logic [2:0] DataValid;
  logic [2:0] nextDataValid;
  
  //Act on control signals in the data phase
  // define the bits in the status register
  assign Status = { 29'd0, DataValid};
  • 信号输入成功,计数便加一,同时把datavalid拉起来
  • read_enable为1表示正在读取这个寄存器,把DataValid置0不让读。
//nMode
        if ( 信号输入成功)
          begin
            counter_0 <= counter_0 + 1;
            DataValid[0] <= 1;
          end
       else if ( read_enable && ( word_address == 0 ) )
          begin
            DataValid[0] <= 0;
          end

4. 写入数据HWDATA

  • HWDATA没有经过interconnect模块,直接连接到各个子模块里;

  • 控制信号write_enable为高时才可写;

可写的子模块中
        if ( write_enable && (word_address==0))
          begin
            Mode_counter <= HWDATA;
            Mode_write_DataValid <= nextDataValid[0];
            nextDataValid[0]<= 0;
         end
        else if( write_enable && (word_address==3))begin   //status寄存器
            nextDataValid[0] <= HWDATA[0];
            Mode_write_DataValid <= 0;
            end

  片选成功的信号进入子模块,当片选信号拉起,同时也将这个HADDR也传入到子模块里,用于确定子模块中IO寄存器。

//slave module:ahb_CUSTOM.sv
    else if ( HREADY && HSEL && (HTRANS != No_Transfer) )
      begin
        write_enable <= HWRITE;
        read_enable <= ! HWRITE;
        word_address <= HADDR[4:2];
     end

  这里需要注意的是,HREADY拉高、HSEL拉高的时候才能写入值。还记得上面C代码中如果想往IO寄存器中写入数据,需要先往STATUS寄存器中写入1

5. 读写控制HWRITE

  HWRITE是一个在读写控制中使用的信号,用于指示当前传输是否是写操作。

  HWRITE则用来区分是读取操作还是写入操作。如果HWRITE为高电平,表示这是一个写操作;如果为低电平,表示这是一个读操作。

在这里插入图片描述

  • 如果HREADY为高电平而HWRITE为低电平,那么在下一个时钟周期,接口可以开始新的读操作。
  • 如果HREADY为高电平HWRITE为高电平,那么下一个时钟周期总线将进行写入操作。
  • 如果HREADY为低电平而HWRITE为高电平,这表示当前正在进行一个写操作,但操作还未完成。

  ps,在读取IO寄存器之前应该先检查Status寄存器中对应bit的值是否为1

  //Generate the control signals in the address phase
  always_ff @(posedge HCLK, negedge HRESETn)
    if ( ! HRESETn )
      begin
        read_enable <= '0;
      end
    else if ( HREADY && HSEL && (HTRANS != No_Transfer) )
      begin
        read_enable <= ! HWRITE;
     end
    else
      begin
        read_enable <= '0;
     end

6. HREADY

  HREADY 是一个握手信号,用于指示当前事务是否已经结束。如果HREADY为高电平,那么在下一个时钟周期,接口可以开始新的事务。如果为低电平,那么它表明当前事务还没有结束,接口需要在下一个时钟周期继续进行同一个事务。

  interconnect模块中传出HREADY,HREADY信号用于指示数据传输的就绪状态,确保数据的有效传输和协议的正确执行。

  always_comb
    begin
      // default values
      HREADY = 1;
      HRDATA = 32'hDEADBEEF; // "hexspeak" to indicate an error has occured
      
      // since num_slaves is a parameter all of this should be unrolled at compile time
      
      for ( i = 0; i < num_slaves; i++ )
        if ( mux_sel == (1 << i) )
          begin
            HREADY = HREADYOUT_SIGNALS[i];
            HRDATA = HRDATA_SIGNALS[i];
          end

    end

7. 数据有效信号:DataValid和NextDataValid用于减少由于异步更新带来的可能问题

  这个是iain设计的一种机制,当datavalid为高电平,表示数据已经准备好并且是有效的;为低电平,则表示数据还未准备好或者无效。

  在这个SoC中代码片段,DataValidNextDataValid信号看起来是用来控制和同步数据输出的。根据给出的注释,我的理解是这样的:

  1. DataValid: 这个信号表明DataOut寄存器当前的数据是否有效。当DataValid为1时,意味着DataOut的数据是有效的,可以被其他设备或模块使用。当DataValid为0时,意味着DataOut的数据是无效的,不应被其他设备或模块使用。
  2. NextDataValid: 这个信号预设了下一次DataOut更新后的DataValid状态。也就是说,当你写入DataOut寄存器时,DataValid将被设置为NextDataValid的当前值。这样做可以在DataOut数据更新的同时更新其有效性状态,减少了由于异步更新带来的可能问题。

  关于使用这两个寄存器的顺序,注释中也给出了建议:首先更新NextDataValid寄存器,然后再更新DataOut寄存器。这样做可以确保当数据被写入DataOut寄存器时,DataValid立即得到正确的更新。

// Address map :
//   Base addess + 0 : 
//     Read DataOut register
//     Write DataOut register, Copy NextDataValid to DataValid
//   Base addess + 4 : 
//     Read Status register
//     Write NextDataValid register
//
// Bits within status register :
//   Bit 1   NextDataValid
//   Bit 0   DataValid


// In order to update the output, the software should update the NextDataValid
// register followed by the DataOut register.


  //Act on control signals in the data phase

  // write
  always_ff @(posedge HCLK, negedge HRESETn)
    if ( ! HRESETn )
      begin
        DataOut <= '0;
        DataValid <= '0;
        NextDataValid <= '0;
      end
    else if ( write_enable && (word_address==0))
      begin
        DataOut <= HWDATA;
        DataValid <= NextDataValid;

        // this is not synthesized but provides useful debugging information
        if ( NextDataValid )
          $display( "DataOut: ", HWDATA, " @", $time );
        else
          $display( "DataOut:--Invalid-- @", $time );

     end
    else if ( write_enable && (word_address==1))
      begin
        NextDataValid <= HWDATA[0];
     end
     
  // define the bits in the status register
  assign Status = { 30'd0, NextDataValid, DataValid};

  在给出的硬件描述语言(HDL)代码中,你可以看到这个行为的实现。当执行写入操作且地址指向DataOut寄存器(word_address==0)时,DataOut被设置为输入数据(HWDATA),DataValid被设置为NextDataValid的当前值。当地址指向NextDataValid寄存器(word_address==1)时,NextDataValid被设置为输入数据的最低位(HWDATA[0])。注意这里假设NextDataValid只需要1位数据。

  另外,Status寄存器的最低两位被定义为NextDataValidDataValid,以方便读取它们的状态。

为什么这样可以减少由于异步更新带来的可能问题

  考虑一种情况,即先更新DataOut,然后再单独更新DataValid。在这种情况下,可能存在一个短暂的时期,数据已经被更新,但DataValid标志还没有被更新。在这个短暂的时期内,其他设备或模块可能会错误地认为DataOut的新数据是无效的,或者错误地使用旧数据,因为DataValid标志还没有反映出数据的新状态。

  为了避免这个问题,我们可以使用NextDataValid来预先设定DataOut更新后的DataValid状态。这样,当我们写入DataOut时,DataValid也会立即更新,确保DataValid始终与DataOut的状态同步。这就避免了由于DataOutDataValid之间的异步更新而可能产生的问题。

iain - ARM System on Chip (ASIC version)

  ps,我们最后在FPGA板级验证的时候报了个错,只按一下trip清零清不了distance,只有前轮再转一圈才能清零。我们trip清零写在了sv代码里,所以底层的寄存器肯定清零了。但是有可能是因为trip的数据已经更新了,但是负责LED的寄存器没有清空,他错误地使用了旧的LED的数据,因为LED显示的数据是在c语言里先从寄存器读出来再写进去,说明没有读到清零的值。

  ps,Iain在他的网页introductory里放的是nonspecific interface 让你可以学习,我们要自己改成custom interface。

五、后记

  在第一次尝试做SoC设计的时候,我们组精准地踩了很多坑,一脸懵逼、次次被Iain劝退。但是感谢团队所有人,我们最终完成了非常好的SoC设计。

第一次被怼:Draft Design Proposal feedback: “It’s all wrong. Completely Wrong(^ - ^)…And you’re saying how to correct this, and What I’m saying is, DON’T DO THIS!.. It’s looks like you haven’t understood the system on chip concept”.

第二次被怼:Design Proposal feedback: “He won’t necessarily spot that he’s writing rubbish…that makes no sense…Given the structure suggested in figure one, I think you might be better of building a hardware only design…”.

(”TOTALLY IN WRONG DIRECTION“——Iain)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/661165.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python如何隐藏输出

Python如何隐藏输出 如果你是一个Python程序员&#xff0c;你可能会遇到需要隐藏输出的情况。这可能是因为你的输出包含敏感信息&#xff0c;或者你只是想使你的输出更加整洁&#xff0c;避免不必要的干扰。在这篇文章中&#xff0c;我们将探讨一些方法来隐藏Python中的输出。…

微擎PHP文件goto解密源码工具

&#x1f389; 有需要的朋友记得关赞评&#xff0c;文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 1、php 版本需要大于 7 2、需要解密的文件放到 decodeFile 下&#xff0c;解密至 complete 下 3、解密的可以是多个文件&#xff0c;不能放…

操作系统-I/O管理-I/O管理概述

目录 一、I/O设备的分类 1.1分类 1.1.1特性分类 1.1.2按传输速率分类 1.1.3按信息交换单位分类 二、I/O控制器 三、I/O控制方式 3.1程序直接控制方式 3.1中断驱动方式 3.3DMA方式 3.4通道控制方式 四、I/O软件层次结构 五、输入输出管理 5.1输入输出应用程序接口 …

UE4的AI行为树基础知识

1.前言 在制作游戏时&#xff0c;会制作敌人、怪物、NPC等不被玩家所操作的对象&#xff0c;那么制作这些对象&#xff0c;就需要通过使用AI行为树来为他们编写各自的一些行为逻辑&#xff0c;比如敌人会寻找主角并攻击、怪物会在自己的领域巡逻等等。 2.组件说明 NavMeshBou…

国产麒麟服务器等保二级 配置规范(二)

一、redis的配置规范 1.1 禁止以root账号运行redis服务 以下Linux 命令操作创建了一个无 home 目录权限&#xff0c;且无法登录的普通账号redis。 #useradd -M -s /sbin/nologin redis 修改服务允许和配置文件权限&#xff1a; #setsid sudo -u redis /usr/bin/redis-serer /e…

Python 智能语音机器人(改进版)

本篇为改进版&#xff0c;之前部分代码存在错误&#xff0c;部分网站api也已经失效&#xff0c;现在更换api&#xff0c;并对部分代码进行重写。 本次在Pycharm上测试 相关模块如下&#xff1a; baidu-aip4.16.11 beautifulsoup44.12.2 chardet5.1.0 lxml4.9.2 PyAudio0.2.13…

Redis原理 - RedisObject对象机制

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis原理 - RedisObject对象机制 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-object.html 为什么会设计RedisObject 在 Redis 中&#xff0c;我们的操作都是使用指令进行&#xff0c;而这些的命…

chatgpt赋能python:Python截取文件名教程

Python截取文件名教程 在Python编程中&#xff0c;经常需要从文件路径中截取出文件名。这可以让我们更容易地操作文件或数据集&#xff0c;并且使代码更加简洁易懂。在本教程中&#xff0c;我们将介绍如何使用Python截取文件名。 使用os模块 Python的os模块提供了许多与文件…

【C数据结构】静态顺序表_SeqList

目录 【1】数据结构概述 【1.1】什么是数据结构&#xff1f; 【1.2】数据结构分类 【1.3】数据结构术语 【2】数据结构特点 【3】静态顺序表 【3.1】静态顺序表概念及结构 【3.2】静态顺序表定义数据结构和接口 【3.3】静态顺序表初始化 【3.4】静态顺序表头插入 【…

C/C++内存管理(newdelete详解)

C/C内存管理 引言new与delete的使用内置类型自定义类型 new与delete的实现原理内置类型了解operator new与operator delete函数自定义类型 定位newnew&delete与malloc&free的区别总结 引言 在之前的C语言部分&#xff0c;我们已经介绍过内存中的存储区域的划分&#x…

FMQL在linux下GPIO的映射关系与使用

最近几年国产化芯片的使用如火如荼&#xff0c;部分国产手册说明杂且描述不清&#xff0c;尤其是一些映射关系使用文字描述非常不直观。博主在使用GPIO功能输出的时候反复看了几遍再去尝试控制耗费了不少时间。现结合相关文档总结GPIO映射表格&#xff0c;方便读者使用。 1、 …

【Spring Boot 】Spring Boot 统一功能处理

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 前言 1. Spring 拦截器 1.1 自定义拦截器 1…

6月19日第壹简报,星期一,农历五月初二

6月19日第壹简报&#xff0c;星期一&#xff0c;农历五月初二坚持阅读&#xff0c;静待花开1. 未来10天江南华南等地多降雨过程&#xff0c;国家防总将防汛四级响应范围扩至八省份&#xff0c;加派工作组赴地方协助指导。2. “一天有四季、十里不同天”&#xff0c;“最美”独库…

LVS - DR集群

LVS - DR集群 数据包流向分析&#xff1a; &#xff08;1&#xff09;客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 &#xff08;2&#xff09;Direct…

【VS2022】win 10 / win 11:Visual Studio 2022 社区版免费下载与安装

目录 一、Visual Studio 2022 下载 二、Visual Studio 2022 安装 三、Visual Studio 2022 快捷方式创建 四、Visual Studio 2022 使用 一、Visual Studio 2022 下载 Visual Studio 2022 官方下载https://visualstudio.microsoft.com/zh-hans/downloads/首先登陆 Visual Stu…

attention unet + cldice 论文总结

Blood Vessel Segmentation from Low-Contrast and Wide-Field Optical Microscopic Images of Cranial Window by Attention-Gate-Based Network论文总结 论文&#xff1a;Blood Vessel Segmentation by Attention-Gate-Based Network 目录 一、论文背景和出发点 二、创新点…

vue源码阅读之Observer

我们上次学习了vue数据驱动的概念&#xff0c;以及简单的vue怎么知道数据更新&#xff0c;然后采取行动的。今天我们就来继续深入学习&#xff0c;vue怎么把数据和视图给绑定在一起的&#xff0c;数据发生变化&#xff0c;视图怎么会自动发生变化的。 vue中的Observer 之前讲…

chatgpt赋能python:Python截取某一段文字

Python截取某一段文字 Python是最流行的编程语言之一&#xff0c;用于开发各种类型的应用程序&#xff0c;包括Web应用程序、桌面应用程序、游戏等。在本文中&#xff0c;我们将讨论如何使用Python截取某一段文字。这对于Web开发者和SEO专家非常有用&#xff0c;因为他们需要查…

【干货】Android系统定制基础篇:第八部分(增加以太网设置菜单、支持多摄像头、替换默认签名)

一、增加以太网设置菜单 Android 系统设置默认并没有以太网相关设置项&#xff0c;但以太网功能是支持的&#xff0c;因此我们仅仅需要增加设置界面即可。以太网设置界面如下&#xff1a; 修改 diff --git a/packages/apps/Settings/AndroidManifest.xml b/packages/apps/Se…

【第六次】21级计科计算机组成原理课外练习

【第六次】21级计科计算机组成原理课外练习 一、单选题二、多选题三、填空题 一、单选题 2-1 假定某计算机按字节编址&#xff0c;采用小端方式&#xff0c;有一个float型变量x的地址为0xffffc000&#xff0c;x12345678H&#xff0c;则在内存单元0xffffc001中存放的内容是 A.…