【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统;实现NoC的RTL设计

news2024/11/15 7:28:25

【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统;实现NoC的RTL设计

  • 0. NoC多核片上网络生成器
    • ProNoC
    • ProNoC的功能实现
  • 1. 生成一个叫做Mor1kx SoC的单个Tile(包含NI网络接口)
    • 1.1 打开ProNoC用于生成Tile的GUI界面
    • 1.2 为Tile添加时钟IP
      • 参数类型:localparameter和parameter和Don't include
    • 1.3 为Tile添加wishbone总线IP
    • 1.4 添加其他的IP:Mor1kx核、RAM、Timer、jtag、GPIO
      • SoC里的几个IP的分析
      • 配置NI网络接口给wishbone总线的SoC
    • 1.5 检查SoC上在内存占用的地址映射
    • 1.6 检查SoC的功能连接图
    • 1.7 为FPGA上定义时钟(不用做)
    • 1.8 报错分析 Gtk3::Gdk::Window失效
    • 1.9 生成这个基于Mor1kx内核的SoC的RTL代码
  • **2. 使用SoC搭建一个4核的片上网络系统**
    • 2.1 配置一个2x2的mesh型的NoC网络结构,使用xy路由协议
    • 2.2 配置NoC网络的各个处理器核/SoC/CPU/GPU/纯内存
    • 2.3 从Diagram查看生成的NoC based MPSoC的结构图
    • 2.4 另存为mor1k_mpsoc.mpsoc作为配置文件
    • 2.5 为FPGA生成时钟(不用做)
    • 2.6 生成RTL代码
  • 3. NoC based MPSoC 级的软件编程
    • 3.1 每个Tile的软件程序转译到SoC的RAM里实现功能
      • main.c在每个Tile里编写
      • main.c解析
    • 3.2 使用verilater进行编译和仿真
    • 3.3 生成Testbench.cpp作为测试文件
      • Testbench.cpp文件
    • 3.4 ==仿真结果==
    • 报错
  • 4. 综合
    • 4.1 用quartus ii 综合生成后的Mor1kx的RTL代码
      • Debug记录
    • 4.2 综合2x2的mor1k_mpsoc

0. NoC多核片上网络生成器

  实现NoC的RTL设计一直是很复杂的,而NoC生成器(Network-on-Chip Generator)是一种用于设计和生成Network-on-Chip架构的工具。它可以根据用户的需求和参数,如网络的尺寸、拓扑结构、路由算法等,自动生成具有所需特性的NoC设计。主要功能包括自动生成和优化NoC设计以及进行性能分析和验证。简而言之,NoC生成器可以帮助实现更高效且可靠的系统集成设计。

ProNoC

  ProNoC有一个很简洁的GUI界面。

  1. 如果想生成一个SoC,只需要选择内核(有很多种软核)、wishbone总线、内存等IP并设置好参数和大小,ProNoC工具链就可以“连连看”把一个SoC的RTL代码生成出来。ps,未来ChatGPT生成RTL代码指日可待。
  2. 如果想生成一个MPSoC多核系统,选择期待用到的SoC并用统一的NoC模块连接,因为NI出去之后是Router。NoC的通信即通过Router之间的通信和路由。
  3. ProNoC甚至提供了可以烧录FPGA的功能。
    在这里插入图片描述
  4. ProNoC - User Manual用户手册
  5. ProNoC - OpenCores
  6. ProNoC - Github
  7. ProNoC - User Manual(github的一版)
  8. ProNoC_System_Overview
  9. Monemi A, Tang J W, Palesi M, et al. ProNoC: A low latency network-on-chip based many-core system-on-chip prototyping platform[J]. Microprocessors and Microsystems, 2017, 54: 60-74.

ProNoC的功能实现

  除了配置NoC的结构,在一个多处理器系统上芯片(MPSoC)中,每个处理器核心(也叫内核)都会有它自己的一份代码,这份代码通常是用C或者其他高级语言编写的。这份代码定义了处理器核心的功能和行为。

  在NoC的环境中,这些处理器核心通过网络接口(Network Interface,NI)与网络进行通信。每个内核都有一个或多个NI,这些NI负责将内核的通信需求转化为网络可以理解的消息。

  C/C++编程就是描述了每一个内核输出信息的动作,包括输出内容、格式和目的地,然后通过NI(Network Interface)接口输出的标准通信信息(Flits组成Packet)。将C/C++转译为该SoC内核理解的二进制代码存储到RAM里,这些代码描述了内核如何处理数据、执行任务,以及如何通过其网络接口(Network Interface,NI)生成和发送通信信息。
在这里插入图片描述

  除了每个内核的main.c编程以外,在PC上做仿真时,还需要编写Testbench.cpp(用于Verilator联合仿真)或Testbench.v(modelsim)为测试提供激励。

1. 生成一个叫做Mor1kx SoC的单个Tile(包含NI网络接口)

  按照官网用户手册的 《CHAPTER 5. Processing Tile Generator Hello World Tutorial》生成单个Processing Tile以用来搭建多核网络。

1.1 打开ProNoC用于生成Tile的GUI界面

cd ~/ProNoC/mpsoc/perl_gui
perl ./ProNoC.pl

  进去以后,在上侧的菜单栏选择“Processing tile generator”。

  在左侧的树视图窗口中,您可以看到所有可用 IP 类别的列表。 单击每个类别展开相关的 IP 核列表。 每个 IP 核都可以通过双击其名称添加到 GUI。

  1. 在第一列中,您可以在 GUI 界面中上下移动 IP 核框位置,移除 IP 核或设置其参数(如果有)。
  2. 在第二列中,您可以重命名 IP 内核实例名称。
  3. 第三列显示该模块的所有 (Plug) 接口。 在这里您可以将每个插头连接到一个合适的(插座)接口。 (每个接口分为插头和插座两种类型。有关接口的更多信息,请参阅接口生成器章节。您也可以在此处将接口导出为 SoC 的输入/输出 (IO) 端口。

1.2 为Tile添加时钟IP

  1. 在左侧 “IP list” 里选择:Source 双击clk_source即可添加时钟IP
  2. 并重命名第二列的“Instance Name”为:`clk_source。
  3. 将clk和reset的类型选择为==IO==。
    在这里插入图片描述

参数类型:localparameter和parameter和Don’t include

  在设置IP的时候可以看到,参数的选项有三个:localparameterparameter

  localparamparameter是Verilog硬件描述语言中的两个关键字,它们都用于定义参数,但在用法和应用场景上有所不同。

  parameter是Verilog中的常数,一般用于模块间传参,即允许在实例化模块的时候设置参数的值。这就意味着,如果你选择把它定义为parameter,那么在网络-on-chip(NoC)基础上的MPSoC生成时,你就可以更改处理tile参数的值,允许在不同地方以不同的参数值调用相同的tile。

  与此相反,localparam定义的是局部参数,这些参数不能从模块外部更改。一旦在模块内部被定义,它们的值就固定了,无法在模块实例化时更改。

  所以,何时使用localparamparameter主要取决于你的需求:

  • 如果你需要在你的network-on-chip(NoC)设计中,为相同的处理tile实例赋予不同的参数值,那么应该使用parameter
  • 如果你有一些固定的参数值,这些值在定义后不需要更改,或者你不希望这些值被外部模块更改,那么应该使用localparam

  关于"不包括"(Don’t include)的选择,如果参数是一个需要在软件代码中使用的软件参数,你可以选择这个选项。这样,参数将不会在生成的Verilog文件中被定义,而是在软件代码中定义和使用。

1.3 为Tile添加wishbone总线IP

  双击Bus底下的wishbone_bus可添加重新命名为wishbone_bus,都选为localparam并设为如下参数

  • M是master interfaces number,设为4;其中分别为:内核的instruction write back、内核的data write back、Network Interface的wb_send、Network Interface的wb_receive;

  • S是slave interfaces number,设为4;其中分别为RAM的wb、Network Interface的wb_slave、timer的wb、uart的wb;

  • Dw是Data width,设为32;

  • Aw是Address width,设为32;
    在这里插入图片描述

1.4 添加其他的IP:Mor1kx核、RAM、Timer、jtag、GPIO

  按照下图设置:图片里的名字没有完全对上,比如clk_source:clk;

  官方文档图里标黄的地方的slave的号没对上,我这里设置了:

  • slave[0]连RAM

  • slave[1]连NI

  • slave[2]连Timer

  • slave[3]连uart
    在这里插入图片描述

  • M是master interfaces number,设为4;其中分别为:内核的instruction write back、内核的data write back、Network Interface的wb_send、Network Interface的wb_receive;

  • S是slave interfaces number,设为4;其中分别为RAM的wb、Network Interface的wb_slave、timer的wb、uart的wb;

设置后如图所示,参数类型我都选的localparam
在这里插入图片描述

在这里插入图片描述

SoC里的几个IP的分析

值得注意的是,

  • 在Processer的Mor1kx核设置时:iwb(instruction write back)连上了wishbone总线的**master[0]**口;(wishbone_bus:wb_master[0])
  • 在Processer的Mor1kx核设置时:dwb(data write back)连上了wishbone总线的**master[1]**口(wishbone_bus:wb_master[1]);
  • 在单端口RAM的设置中:wb(write back)连上了wishbone总线的**slave[0]**口(wishbone_bus:wb_slave[0]);
  • 在NI网络接口的设置中:wb_send连上了wishbone_bus:wb_master[2];wb_receive连上了wishbone_bus:wb_master[3];wb_slave连上了wishbone_bus:wb_slave[1]; interrupt连上了mrk1kx核的中断口[1](mor1kx:interrupt_peripheral[0]);
  • 在Timer定时器的设置中:wb(write back)连上了wishbone总线的**slave[2]**口(wishbone_bus:wb_slave[2]); intrp(interrupt)连上了mrk1kx核的中断口[1](mor1kx:interrupt_peripheral[1]);
  • 在ProNoC_jtag_uart(实例名字叫uart)的设置中,wb_slave连上了wishbone总线的**slave[3]**口(wishbone_bus:wb_slave[3]);

配置NI网络接口给wishbone总线的SoC

  请注意,本章中所需的磁贴具有要连接到 NoC 的网络接口 (NI) IP。

在这里插入图片描述

  • NI 有一个主接口,可以自动将NoC 到达的数据包写入主存储器。
  • 因此,对于启用了数据缓存的 CPU,您需要支持探听(以使 NI 写入的数据缓存内存位置无效)或将 NI 接收器缓冲区映射到不可缓存的内存位置。
  • 对于这个例子,我们启用了 Mor1kx 处理器的监听支持。 CPU的snoop接口必须连接到WB,通知主存数据变化。

1.5 检查SoC上在内存占用的地址映射

  添加所有需要的 IP 内核后,现在您可以通过单击 Wishbone-bus addr 按钮是==WB addr==检查自动分配的 Wishbone 总线地址。这些地址是根据 IP 内核库设置、插入的参数和系统中重复的相同 IP 内核的数量自动设置的。 但是,您可以随意将它们调整为新值,因为插入的地址没有冲突。

  我使用了默认的值。

在这里插入图片描述

  ps, instance name前的那个序号0:uart的序号顺序好像没什么影响。

1.6 检查SoC的功能连接图

  点击 Diagram 按钮观察 SoC 功能框图,上面有三个选项:移除未连接的实例、移除clk的连接、移除reset的连接。可以得到下图的连接关系,很方便。

在这里插入图片描述

1.7 为FPGA上定义时钟(不用做)

  如果计划将处理块用于MPSoC 内的内部模块,则可以忽略时钟设置

  如果希望SoC成为FPGA实现中的顶层模块(例如,在此示例生产的RTL代码),您可能需要从 FPGA 输入参考时钟生成 SoC 时钟信号。

  为此,请单击 CLK 设置按钮。 它将打开一个新窗口,您可以在其中将 SoC 顶级模块连接到某些时钟源。 例如 Xilinx Kintex-7 FPGA KC705 有一个差分参考时钟。 首先需要使用 IBUFGDS 模块将差分 FPGA 输入时钟转换为单个时钟。 IBUFGDS (diff:clk) 的输出为 200 MHz,对于所需的 SoC 来说太高了,无法满足时序约束。 该时钟可以使用 PLL 进行二分频。 为此,将 PLL 倍频 (CLKFBOUT_MULT) 和分频因子 (CLKOUT0_DEVIDE) 分别设置为 9 和 18

  • 请注意,如果您希望直接将FPGA 参考时钟连接到SoC,则可以省略此配置。 TOP 模块的时钟和复位信号默认保留为输出。

  • 如果计划将处理块用于MPSoC 内的内部模块,则可以忽略时钟设置。

1.8 报错分析 Gtk3::Gdk::Window失效

  报错记录:我在使用gui生成tile的时候选择参数有弹出窗口,点击“X”关闭弹出的窗口之后发现gui界面里的一部分设置界面没了,然后报这个错误。

报错描述

perl ./ProNoC.pl
***  unhandled exception in callback:
***  undef is not of type Gtk3::Gdk::Window at /usr/lib/x86_64-linux-gnu/perl5/5.30/Glib/Object/Introspection.pm line 67.
***  ignoring at /home/ubuntu/perl5/lib/perl5/Gtk3.pm line 572.

  报错分析:当你关闭GUI窗口时,GTK3的窗口和相关的资源会被自动销毁。如果你在这之后尝试访问这些资源(比如,调用一个方法或者获取一些属性),你会遇到这个错误,因为你正在试图操作一个不存在的窗口。

尝试解决方法:

  1. 如果是在输入perl ./ProNoC.pl直接打不开gui界面且报这个错误,请尝试用ubuntu20.04系统而不是22.04。(亲测有效)
  2. 在关闭弹出的窗口时尽量点“ok”而不是右上角的“x”,让回调函数可以往回去传值而不是中止函数运行。
  3. 出现了错不要慌,把现有的设置先保存下来,下次再打开的时候,直接load file,可以省略很多步骤。

在这里插入图片描述

1.9 生成这个基于Mor1kx内核的SoC的RTL代码

  1. 设置Tile名称为==mor1k_tile==。

  2. Generate RTL按钮生成这个Tile的RTL代码。

在这里插入图片描述

  至此,一个Tile已经搭建完毕了。接下来尝试搭建2x2的NoC网络。

2. 使用SoC搭建一个4核的片上网络系统

  在官网用户手册第七章《CHAPTER 7. Simple message passing demo on 2×2 MPSoC》

  按照下图,使用我们做好的这个Tile搭建NoC网络。

在这里插入图片描述

2.1 配置一个2x2的mesh型的NoC网络结构,使用xy路由协议

  重新启动ProNoC用于生成Tile的GUI界面。进去以后,在上侧的菜单栏选择“NoC based MPSoC generator”。

cd ~/ProNoC/mpsoc/perl_gui
perl ./ProNoC.pl

在这里插入图片描述

  • 上图的minimum packet size 应为3

  按照表 7.2 设置 NoC 配置设置。 在这里,我们定义了两个虚拟网络 (VN, Virtual Network),方法是定义两个消息类别并分离消息类别允许的 VC,每个消息类别只能使用其自己的专用 VC。

在这里插入图片描述

2.2 配置NoC网络的各个处理器核/SoC/CPU/GPU/纯内存

  在Tile configuration setting里设置Tile的类型,每个Tile是我们自己的添加进去的核,一般自动生成的会放在lib/soc底下。

  您应该能够看到在其共享总线中具有 NI IP 核的所有处理 tile 模块的列表,在这里设置NoC网络里各种各样的核是什么,将 Mor1k_tile设置为 0,1,2,3或简单地设置为 0:3。即,NoC网络具有16个SoC,每个SoC的类型是Mor1k_tile。也可以通过简单地单击 NoC GUI界面上的在各个位置上的Tile,将生成的处理Tile映射到它们的位置。

在这里插入图片描述

  按下 OK 按钮,它会显示一个窗口,您可以在其中更改默认参数值。具有自定义参数设置的磁贴在其名称上标有 *。

2.3 从Diagram查看生成的NoC based MPSoC的结构图

  点击Diagram按钮查看结构框图如下:

在这里插入图片描述

  还可以查看NoC网络的参数

在这里插入图片描述

2.4 另存为mor1k_mpsoc.mpsoc作为配置文件

在这里插入图片描述

2.5 为FPGA生成时钟(不用做)

  同1.7所讲

  如果应使用 FPGA clk 引脚生成 MPSoC clk,请单击 CLK
设置按钮然后跟随 CLK 设置以生成 MPSoC 时钟。

2.6 生成RTL代码

Click on Generate RTL button to generate the MPSoC RTL code.

在这里插入图片描述

3. NoC based MPSoC 级的软件编程

3.1 每个Tile的软件程序转译到SoC的RAM里实现功能

  • 点击software进行软件编程使用C代码

  • 在左边的Tree-View窗口中,可以选择工程sw目录下的任意一个文件打开,然后进行编辑。 用以下 C 代码替换所有图块中 main.c 文件的内容。
    在此示例中,tile 0 到 2 将每 3 个数据包发送到 tile 3。 tiles 3 显示串口终端中的数据包内容。

在这里插入图片描述

main.c在每个Tile里编写

  直接复制粘贴到每个Tile里的main函数上。现在按下编译按钮。 这使用 Mor1kx GNU 工具链编译 C 代码。 如果一切运行正常,您一定会看到“编译成功完成”消息。 否则,请检查错误消息以修复您的代码并再次按下编译按钮。

#define MULTI_CORE
#include "mor1k_tile.h"

unsigned char pck1[10] = "first data";
unsigned char pck2[11] = "second data";
unsigned char pck3[6] = "123456";
unsigned char receive_buff[ni_NUM_VCs][16];

// a simple delay function
void delay(unsigned int num) {
    while (num > 0) {
        num--;
        nop();
    }
}

void error_handelling_function() {
    unsigned int i;
    for (i = 0; i < ni_NUM_VCs; i++) {
        if (ni_got_buff_ovf(i)) {
            printf("VC%u: The receiver allocated buffer size is smaller than the received packet size in core%u\n", i, COREID);
            ni_ack_buff_ovf_isr(i);
        }
        if (ni_got_send_dsize_err(i)) {
            printf("VC%u: The send data size is not set in core%u\n", i, COREID);
            ni_ack_send_dsize_err_isr(i);
        }
        if (ni_got_burst_size_err(i)) {
            printf("VC%u: The burst size is not set in core%u\n", i, COREID);
            ni_ack_burst_size_err_isr(i);
        }
        if (ni_got_invalid_send_req(i)) {
            printf("VC%u: A new send request is received while the DMA is still busy sending the previous packet in core%u\n", i, COREID);
            ni_ack_invalid_send_req_isr(i);
        }
        if (ni_got_crc_mismatch(i)) {
            printf("VC%u: CRC mismatched in core%u\n", i, COREID);
            ni_ack_crc_mismatch_isr(i);
        }
    }
}

void got_packet_funtion(void) {
    unsigned int i;
    // unsigned char iport;
    for (i = 0; i < ni_NUM_VCs; i++) {
        if (ni_got_packet(i)) {
            // iport = ni_RECEIVE_PRECAP_DATA_REG(i);
            // different destination can be targeted according to iport value
            // E.g if (iport == 0) ini_receive(i, (unsigned int)&receive_buff_p0[i][0], 16, 0);
            // E.g else if (iport == 1) ini_receive(i, (unsigned int)&receive_buff_p1[i][0], 16, 0);
            ni_receive(i, (unsigned int)&receive_buff[i][0], 16, 0);
            ni_ack_got_pck_isr(i);
        } // If ni got packet
    }
}

void check_packet_funtion(void) {
    // unsigned char iport;
    unsigned int i, j, size;
    struct SRC_INFOS src_info;
    for (i = 0; i < ni_NUM_VCs; i++) {
        if (ni_packet_is_saved(i)) {
            src_info = get_src_info(i);
            size = ni_RECEIVE_DATA_SIZE_REG(i);  // size in bytes
            // iport = src_info.r;
            // run a function on the received packet according to the destination port
            // E.G func_on_received_packet(iport);
            // but here we just print the received packet in the terminal
            printf("A message of %u bytes is received from core (%x) in vc%u:", size, src_info.addr, i);
            for (j = 0; j < size; j++) {
                printf("%c", receive_buff[i][j]);
            }
            printf("\n");
            ni_ack_save_done_isr(i);
        } // If ni_packet_is_saved
    }
}

void sent_packet_done_funtion(void) {
    // unsigned char oport;
    unsigned int i;
    for (i = 0; i < ni_NUM_VCs; i++) {
        if (ni_packet_is_sent(i)) {
            ni_ack_send_done_isr(i);
        } // If ni_packet_is_sent
    }
}

// NI interrupt function
void ni_isr(void) {
    // place your interrupt code here
    if (ni_any_err_isr_is_asserted()) {
        // An error occurred
        error_handelling_function();
    }
    if (ni_any_sent_done_isr_is_asserted()) {
        // check which VC has finished sending the packet.
        sent_packet_done_funtion();
    }
    if (ni_any_save_done_isr_is_asserted()) {
        // check which VC has finished saving the packet. This function must be called before got_packet_funtion
        check_packet_funtion();
    }
    if (ni_any_got_pck_isr_is_asserted()) {
        // check which VC got packet
        got_packet_funtion();
    }
    return;
}

int main() {
    printf("Hi from core %u\n", COREID);
    general_int_init();
    general_int_add(ni_INT_PIN, ni_isr, 0); // ni_INT_PIN
    // Enable ni interrupt (it's connected to interrupt pin 0)
    general_int_enable(ni_INT_PIN);
    general_cpu_int_en();
    // hw interrupt enable function:
    // ni_initial(burst_size, errors_int_en, send_int_en, save_int_en, got_pck_int_en)
    ni_initial(16, 1, 1, 1, 1); // enable the interrupt when a packet is received, saved, or any error occurs
    if (COREID == 3) while (1); // Core 3 only receives packets from other cores

    ni_transfer(1, 0, 0, 0, (unsigned int)&pck1[0], 10, PHY_ADDR_ENDP_3);
    ni_transfer(1, 1, 1, 0, (unsigned int)&pck2[0], 11, PHY_ADDR_ENDP_3);
    ni_transfer(1, 0, 0, 0, (unsigned int)&pck3[0], 6, PHY_ADDR_ENDP_3);

    printf("Total sent packets by core %u is %u\n", COREID, 3);

    while (1) {
    }
    return 0;
}

main.c解析

  这个C语言程序是一个多核(MULTI_CORE)并行计算的程序,针对网络接口(Network Interface,简称NI)进行操作。该程序中使用了MOR1K开放源代码RISC(减少指令集计算)处理器的Tile设备驱动程序。处理器的每一个核心(core)被分配了一个ID(COREID)用于标识。

代码的主要部分可以分为几个模块:

  1. 全局变量的定义:在最开始的部分,定义了几个全局变量,如pck1pck2pck3是需要发送的数据包,receive_buff是一个二维数组,用于存储每个虚拟通道(VC)接收的数据包。数据包大小被设定为16字节。
  2. 辅助函数的定义:定义了几个辅助函数,用于处理各种事件,如错误处理(error_handelling_function),数据包接收(got_packet_funtion),数据包检查(check_packet_funtion)以及数据包发送完成(sent_packet_done_funtion)等。
  3. 中断处理函数:定义了一个网络接口中断处理函数(ni_isr),当中断发生时,它会调用上述的辅助函数来处理事件。中断可能发生在以下几种情况:接收到新的数据包,数据包保存完成,数据包发送完成,以及出现错误等。
  4. main函数:在main函数中,首先初始化并启用了一些基础设置(如打开中断和网络接口)。然后根据COREID的值来决定是否发送数据包。如果COREID等于3,那么这个核心将只接收数据包,不会发送任何数据包。否则,它将会发送数据包pck1pck2pck3。最后进入一个空的无限循环。

  该代码的主要作用是在多核环境下,利用网络接口进行数据包的发送和接收。其中有一些硬件特定的函数和变量(如COREIDni_transfergeneral_cpu_int_en等),可能需要特定的硬件环境和库才能正确编译和运行。

3.2 使用verilater进行编译和仿真

  如果您的系统上安装了 Verilator 软件,您可以在运行您开发的软件时模拟您的 SoC。 为此,请遵循以下说明:

  按下右下角的 Compile RTL 按钮。 这应该打开“选择编译器窗口”,如图 5.17 所示。 选择 Verilator 作为编译器工具,然后按Next

在这里插入图片描述

  使用verilator仿真,同时可以把四个核一起仿真。看到如下提示,再点Next

Veriator model has been generated successfully!

在这里插入图片描述

  • Varilator仿真的一些解释

  这个 Vtile0 类是由 Verilator 从 Verilog 硬件描述语言生成的。通常,这个类会在一个 Verilator 生成的硬件模型中被用到,模型的目标是模拟硬件的行为。具体地,当你有一个使用 Verilog 描述的硬件设计(比如说,一个电路或者一个系统芯片),你可以使用 Verilator 工具将其转换成 C++ 代码。这种转换让你可以在一台普通的电脑上模拟硬件的行为,而不需要任何实际的硬件。

3.3 生成Testbench.cpp作为测试文件

  在点完Next之后,需要点Regenerate Testbench.cpp生成测试文件。这段代码是一个Verilog硬件仿真的测试平台,用于在C++环境下测试ProNoC 。主要功能包括模拟时钟信号、重置信号、使能信号等,并通过仿真的方式,测试一些硬件模块的功能。

  我看了一下代码,这个很像modelsim的功能仿真,只产生时钟、初始化、连接dut。

  我们已经把在NoC中发送信息的代码写到了每个Tile的main.c,这个testbench只提供时钟、reset、初始化信号等。然后打印信息。如果没成功输出结果,很可能是因为SoC、MPSoC的参数设置有问题,也可能是Tile里的RAM的地址不匹配。

在这里插入图片描述

  现在您必须在软件代码编辑窗口中打开 testbech.cpp,如图 5.19 所示。 这是运行生成的 SoC 的最小测试台文件。 它具有连接到时钟和复位信号的 SoC 实例模块。 您可以根据需要编辑此文件。

  testbech.cpp这段代码是使用Verilog硬件描述语言(HDL)模拟四个并联的"tile"或硬件模块的C++测试平台。这些"tile"可能是一些处理器,它们被连接在一起,形成一个网络。

在这里插入图片描述

Testbench.cpp文件

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>

#include <verilated.h>          // Defines common routines

#include "Vtile0.h"
#include "Vtile1.h"
#include "Vtile2.h"
#include "Vtile3.h"

Vtile0	*tile0;	  // Instantiation of tile0
Vtile1	*tile1;	  // Instantiation of tile1
Vtile2	*tile2;	  // Instantiation of tile2
Vtile3	*tile3;	  // Instantiation of tile3

int reset,clk,enable;


#include "parameter.h"
void * tile_chan_out[NE];
void * tile_chan_in[NE];



#define CHAN_SIZE   sizeof(tile0->ni_chan_in)

#define conect_r2r(T1,r1,p1,T2,r2,p2)  \
	memcpy(&router##T1 [r1]->chan_in[p1] , &router##T2 [r2]->chan_out[p2], CHAN_SIZE )

#define connect_r2gnd(T,r,p)\
	memset(&router##T [r]->chan_in [p],0x00,CHAN_SIZE)

#define connect_r2e(T,r,p,e) \
	memcpy(&router##T [r]->chan_in[p], tile_chan_out[e], CHAN_SIZE );\
	memcpy(tile_chan_in[e], &router##T [r]->chan_out[p], CHAN_SIZE )



#include "topology_top.h"
#include "RxDsim.h" // Header file for sending charactor to UART from STDIN

/*
IO type	  port_size	  port_name
 input    tile0->source_clk_in 
 input    tile0->cpu_cpu_en 
 input    tile0->source_reset_in 
 input  7:0   tile0->uart_RxD_din_sim 
 output    tile0->uart_RxD_ready_sim 
 input    tile0->uart_RxD_wr_sim 
 input  ram_J2WBw-1 : 0  tile0->ram_jtag_to_wb 
 output  ram_WB2Jw-1 : 0  tile0->ram_wb_to_jtag 
 input  uart_J2WBw-1 : 0  tile0->uart_jtag_to_wb 
 output  uart_WB2Jw-1  : 0  tile0->uart_wb_to_jtag 
 input  smartflit_chanel_t  tile0->ni_chan_in 
 output  smartflit_chanel_t  tile0->ni_chan_out 
 input  ni_EAw-1   :   0  tile0->ni_current_e_addr 
 input  ni_RAw-1   :   0  tile0->ni_current_r_addr 
 input    tile1->source_clk_in 
 input    tile1->cpu_cpu_en 
 input    tile1->source_reset_in 
 input  7:0   tile1->uart_RxD_din_sim 
 output    tile1->uart_RxD_ready_sim 
 input    tile1->uart_RxD_wr_sim 
 input  ram_J2WBw-1 : 0  tile1->ram_jtag_to_wb 
 output  ram_WB2Jw-1 : 0  tile1->ram_wb_to_jtag 
 input  uart_J2WBw-1 : 0  tile1->uart_jtag_to_wb 
 output  uart_WB2Jw-1  : 0  tile1->uart_wb_to_jtag 
 input  smartflit_chanel_t  tile1->ni_chan_in 
 output  smartflit_chanel_t  tile1->ni_chan_out 
 input  ni_EAw-1   :   0  tile1->ni_current_e_addr 
 input  ni_RAw-1   :   0  tile1->ni_current_r_addr 
 input    tile2->source_clk_in 
 input    tile2->cpu_cpu_en 
 input    tile2->source_reset_in 
 input  7:0   tile2->uart_RxD_din_sim 
 output    tile2->uart_RxD_ready_sim 
 input    tile2->uart_RxD_wr_sim 
 input  ram_J2WBw-1 : 0  tile2->ram_jtag_to_wb 
 output  ram_WB2Jw-1 : 0  tile2->ram_wb_to_jtag 
 input  uart_J2WBw-1 : 0  tile2->uart_jtag_to_wb 
 output  uart_WB2Jw-1  : 0  tile2->uart_wb_to_jtag 
 input  smartflit_chanel_t  tile2->ni_chan_in 
 output  smartflit_chanel_t  tile2->ni_chan_out 
 input  ni_EAw-1   :   0  tile2->ni_current_e_addr 
 input  ni_RAw-1   :   0  tile2->ni_current_r_addr 
 input    tile3->source_clk_in 
 input    tile3->cpu_cpu_en 
 input    tile3->source_reset_in 
 input  7:0   tile3->uart_RxD_din_sim 
 output    tile3->uart_RxD_ready_sim 
 input    tile3->uart_RxD_wr_sim 
 input  ram_J2WBw-1 : 0  tile3->ram_jtag_to_wb 
 output  ram_WB2Jw-1 : 0  tile3->ram_wb_to_jtag 
 input  uart_J2WBw-1 : 0  tile3->uart_jtag_to_wb 
 output  uart_WB2Jw-1  : 0  tile3->uart_wb_to_jtag 
 input  smartflit_chanel_t  tile3->ni_chan_in 
 output  smartflit_chanel_t  tile3->ni_chan_out 
 input  ni_EAw-1   :   0  tile3->ni_current_e_addr 
 input  ni_RAw-1   :   0  tile3->ni_current_r_addr 

*/


unsigned int main_time = 0; // Current simulation time



void connect_clk_reset_en_all(void){
	//clk,reset,enable
		tile0->source_reset_in=reset;
		tile1->source_reset_in=reset;
		tile2->source_reset_in=reset;
		tile3->source_reset_in=reset;

		tile0->source_clk_in=clk;
		tile1->source_clk_in=clk;
		tile2->source_clk_in=clk;
		tile3->source_clk_in=clk;
		
		tile0->cpu_cpu_en=enable;
		tile1->cpu_cpu_en=enable;
		tile2->cpu_cpu_en=enable;
		tile3->cpu_cpu_en=enable;

	connect_routers_reset_clk();	
}

void sim_eval_all(void){
   	routers_eval();
	tile0->eval();
	tile1->eval();
	tile2->eval();
	tile3->eval();

}

void sim_final_all(void ){
	routers_final();	
	tile0->final();
	tile1->final();
	tile2->final();
	tile3->final();
	
}	

void clk_posedge_event(void) {

	clk = 1;       // Toggle clock
	// you can change the inputs and read the outputs here in case they are captured at posedge of clock 
	write_char_on_RXD( );	
	connect_clk_reset_en_all();
	sim_eval_all();
}


void clk_negedge_event(void){

	clk = 0;
	topology_connect_all_nodes_old ();
	connect_clk_reset_en_all();
	sim_eval_all();	
}	

void update_router_st(unsigned int param1, unsigned int param2, unsigned char* param3) {
    // 暂时不做任何事
}
int main(int argc, char** argv) {
	int i,j,x,y;
	printf("There are total of 4 RXD (UART) interface ports in the top module:\n\t0 : tile0_uart_RXD\n\t1 : tile1_uart_RXD\n\t2 : tile2_uart_RXD\n\t3 : tile3_uart_RXD\nThe default interfce is 0. You can switch to different interfaces by pressing + or - key.\n");
	Verilated::commandArgs(argc, argv);   // Remember args
	Vrouter_new();             // Create instance
	
	tile0	=	new Vtile0;
	tile1	=	new Vtile1;
	tile2	=	new Vtile2;
	tile3	=	new Vtile3;

	
	/********************
	*	initialize input
	*********************/
		tile_chan_out[0] = &tile0->ni_chan_out;
	tile_chan_in[0] = &tile0->ni_chan_in;
	tile_chan_out[1] = &tile1->ni_chan_out;
	tile_chan_in[1] = &tile1->ni_chan_in;
	tile_chan_out[2] = &tile2->ni_chan_out;
	tile_chan_in[2] = &tile2->ni_chan_in;
	tile_chan_out[3] = &tile3->ni_chan_out;
	tile_chan_in[3] = &tile3->ni_chan_in;

	
	reset=1;
	enable=1;
	topology_init();
	
	
	
	tile0->ni_current_r_addr=0; // noc->er_addr[0];
	tile0->ni_current_e_addr=0;
	tile1->ni_current_r_addr=1; // noc->er_addr[1];
	tile1->ni_current_e_addr=1;
	tile2->ni_current_r_addr=2; // noc->er_addr[2];
	tile2->ni_current_e_addr=2;
	tile3->ni_current_r_addr=3; // noc->er_addr[3];
	tile3->ni_current_e_addr=3;



	main_time=0;
	printf("Start Simulation\n");
	while (!Verilated::gotFinish()) {
	    capture_char_on_RXD( );
	    if ((main_time & 0x3FF)==0) fflush(stdout); // fflush $dispaly command each 1024 clock cycle 
		if (main_time >= 10 ) 	reset=0;
		
		clk_posedge_event( );
		//The valus of all registers and input ports valuse change @ posedge of the clock. Once clk is deasserted,  as multiple modules are connected inside the testbench we need several eval for propogating combinational logic values 
		//between modules when the clock . 
		for (i=0;i<2*(SMART_MAX+1);i++) clk_negedge_event( );

		main_time++;  
	}//while
	
	// Simulation is done
	sim_final_all();
}

double sc_time_stamp () {       // Called by $time in Verilog
	return main_time;
}
			


3.4 仿真结果

  在此示例中,tile 0 到 2 将每 3 个数据包发送到 tile 3。 tiles 3 显示串口终端中的数据包内容。

得到的输出结果:

在这里插入图片描述

报错

  1. 替换函数名错误;

testbench.cpp: In function void clk_negedge_event():
testbench.cpp:189:2: error: topology_connect_all_nodes was not declared in this scope; did you mean topology_connect_all_nodes_old?
  189 |  topology_connect_all_nodes ();
      |  ^~~~~~~~~~~~~~~~~~~~~~~~~~
      |  topology_connect_all_nodes_old
make: *** [/usr/local/share/verilator/include/verilated.mk:255: testbench.o] Error 1

Compilation failed.

更改代码片段:

void clk_negedge_event(void){
```
clk = 0;
topology_connect_all_nodes_old();
connect_clk_reset_en_all();
sim_eval_all();	
  1. 没有这个函数
update_router_st(unsigned int, unsigned int, unsigned char*)'

在Testbench.cpp里加这个函数,让编译通过。

void update_router_st(unsigned int param1, unsigned int param2, unsigned char* param3) {
    // 暂时不做任何事
}
  1. 更改每一个Tile的RAM的地址大小报错

这种报错。请注意,如果您遇到 RAM 或 ROM 溢出错误,您可以按照链接器 LD 设置修复它们。 如果一切运行成功,您的 sw/tile[n]/RAM 目录中必须有 ram0.bin、ram0.hex 和 ram0.mif 文件,其中 n 是磁贴编号。

The default interfce is 0. You can switch to different interfaces by pressing + or - key.
Start Simulation
Error: The wishbon bus reserved address range width (         14) should be larger than ram width (         20): TOP.tile_0.the_mor1k_tile.ram
%Error: ../src_verilog/lib//./wb_single_port_ram.v:94: Verilog $stop
Aborting...
bash: line 1:  4049 Aborted                 (core dumped) /home/ubuntu/ProNoC/mpsoc_work/MPSOC/mor1k_mpsoc/verilator/obj_dir/testbench

或者这种:

/home/ubuntu/ProNoC/mpsoc_work/toolchain/or1k-elf/bin/or1k-elf-ld: image section `.stack' will not fit in region `ram' /home/ubuntu/ProNoC/mpsoc_work/toolchain/or1k-elf/bin/or1k-elf-ld: region `ram' overflowed by 532 bytes make[1]: *** [Makefile:54: image] Error 1 make: *** [Makefile:5: tile1/.] Error 2

Compilation failed.

  这个错误信息是链接器(ld)告诉你 .stack 区段不会适应 ram 区域,这意味着你的程序尝试使用的 RAM 超出了为其分配的空间。错误还告诉你 ram 区域溢出了 532 字节,这可能是因为你的程序中的全局变量、静态变量或者函数调用堆栈等所占用的空间超出了 RAM 的容量。增加 RAM 区域大小:你可以在链接脚本中增加 RAM 区域的大小,如果硬件允许的话。记得之前Tile设置的是20嘛,他俩得对应起来。

在这里插入图片描述

4. 综合

4.1 用quartus ii 综合生成后的Mor1kx的RTL代码

  可以看到生成的一个Tile是基于wishbone总线,使用Mor1kx内核的SoC,RTL代码很符合规范且可以综合,说明可以用RTL进行下一步FPGA板级验证、门级网表综合

在这里插入图片描述

Debug记录

  1. Error (10054): Verilog HDL File I/O error at output_ports.sv(1): can’t open Verilog Design File “pronoc_def.v”
把所有报错的地方: `include "pronoc_def.v"  换成 `include "./lib/pronoc_def.v"


  1. Error (10839): Verilog HDL error at noc_localparam.v(36): declaring global objects is a SystemVerilog feature
把noc_localparam.v的文件名改成noc_localparam.sv再加入到quartus里

再把lib/src_noc/pronoc_pkg.sv里的定义`include "noc_localparam.v"改成`include "noc_localparam.sv"
  1. Error (10644): Verilog HDL error at debug.v(637): this block requires a name

把这部分第637行改成

genvar i;
generate 
for (i=0;i<V;i=i+1) begin : l1
  1. Error (10052): Verilog HDL error at mor1k_tile_top.sv(275): can’t find port “clk_source_clk_in”

    把这部分代码,注释掉子模块里没有的端口,276行

	the_mor1k_tile 
	(
		.uart_jtag_to_wb(uart_jtag_to_wb),
		.uart_wb_to_jtag(uart_wb_to_jtag),
		.cpu_cpu_en(cpu_cpu_en & jtag_cpu_en),
		.ni_chan_in(ni_chan_in),
		.ni_chan_out(ni_chan_out),
		.ni_current_e_addr(ni_current_e_addr),
		.ni_current_r_addr(ni_current_r_addr),
		.ram_jtag_to_wb(ram_jtag_to_wb),
		.ram_wb_to_jtag(ram_wb_to_jtag),
		//.clk_source_clk_in(TOP_clk_source_clk_in ),
		//.clk_source_reset_in(TOP_clk_source_reset_in | jtag_system_reset)

	);
  1. Error (10232): Verilog HDL error at ni_master.sv(829): index 63 cannot fall outside the declared range [31:0] for vector “m_send_dat_i”

    根据报错,给定明确的定义范围[31:0]

       // assign tail_flit_out   =  m_send_dat_i [Fpay-1 : 0];
		 assign tail_flit_out   =  m_send_dat_i [31 : 0];
  1. Error (10232): Verilog HDL error at ni_master.sv(1047): index 63 cannot fall outside the declared range [31:0] for vector “m_send_dat_i”
                                      (send_tail)? tail_flit_out :  m_send_dat_i [Fpay-1 : 0];
                                      (send_tail)? tail_flit_out :  m_send_dat_i [31 : 0];
  1. Error (10232): Verilog HDL error at ni_vc_wb_slave_regs.v(404): index 46 cannot fall outside the declared range [31:0] for vector “s_dat_i”
//                        if (send_fsm_is_ideal) hdr_data_next = s_dat_i [HDw-1 : 0];
                        if (send_fsm_is_ideal) hdr_data_next = s_dat_i [31 : 0];
  1. Error (12006): Node instance “bse2_inst” instantiates undefined entity “BSCANE2”

把这一段代码注释掉mor1k_tile\src_verilog\lib\jtag_wb\xilinx_jtag_wb.v里

/*
    BSCANE2 #(
        .JTAG_CHAIN(JTAG_CHAIN) // Value for USER command.
    )
    bse2_inst
    (
        .CAPTURE(capture), // 1-bit output: CAPTURE output from TAP controller.
        .DRCK( ), // 1-bit output: Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted.
        .RESET(tlr), // 1-bit output: Reset output for TAP controller.
        .RUNTEST(), // 1-bit output: Output asserted when TAP controller is in Run Test/Idle state.
        .SEL(sel), // 1-bit output: USER instruction active output.
        .SHIFT(shift), // 1-bit output: SHIFT output from TAP controller.
        .TCK(tck), // 1-bit output: Test Clock output. Fabric connection to TAP Clock pin.
        .TDI(tdi), // 1-bit output: Test Data Input (TDI) output from TAP controller.
        .TMS( ), // 1-bit output: Test Mode Select output. Fabric connection to TAP.
        .UPDATE(update), // 1-bit output: UPDATE output from TAP controller
        .TDO(tdo) // 1-bit input: Test Data Output (TDO) input for USER function.
    );
   */ 

4.2 综合2x2的mor1k_mpsoc

  代码Dubug过程和上面一样。可以看到noc_top模块连着四个tile.

在这里插入图片描述

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

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

相关文章

Python基础篇(四):基本数据类型的学习和示例

Python基础篇(三)&#xff1a;基本语句的示例和说明 基本数据类型的学习和操作 1. 数值类型1.1 整数&#xff08;int&#xff09;1.2 浮点数&#xff08;float&#xff09;1.3 复数&#xff08;complex&#xff09; 2. 字符串类型2.1 字符串的定义2.2 转义字符2.3 多行字符串2…

Vue-几种插槽(slot)的使用

插槽(slot) 插槽在vue中是一种很常见的写法&#xff0c;让父组件可以向子组件指定位置插入html结构&#xff0c;也是一种组件间通信的方式 一共有三种分类&#xff1a;默认插槽、具名插槽、作用域插槽&#xff0c;下面一一根据案例改造说明 1 基本案例 首先编写一个基本的案…

12.1 物业管理项目中的新知识点

1.RESTful 2.雪花ID 3.mybatis的动态sql 很多时候需要实现多条件查询&#xff0c;手动判断拼接sql有些麻烦 mybatis提供了一个动态sql实现多条件查询的方法 3.1 if元素 使用if元素可以根据条件来包含或排除某个SQL片段 <select id"search" resultType"Hous…

OpenCV——实验结果输出《图像金字塔》和《图像轮廓》

1.高斯金字塔 img cv2.imread(./AM.png) print(img.shape) upcv2.pyrUp(img) cv_show(up,up)#只是将图片的大小改变了,并且是2倍2倍的增长。 print (up.shape) downcv2.pyrDown(img) cv_show(down,down) print(down.shape) up_downcv2.pyrDown(up) cv_show(up_down,np.hstack…

SSMP整合案例(4) Spring Boot整合MyBatis-Plus实现分页查询

文章 SSMP整合案例(3) 创建数据层并在测试类中运行数据库增删查改操作我们编写了MyBatis-Plus以Druid为数据源的方式 做了数据库的增删查改操作 那么 下面我们继续写 分页查询 我们 先写这样一段代码 bookDao.selectPage()selectPage就是BaseMapper提供给我们的分页查询函数 但…

【Spring Cloud + RabbitMQ 实现分布式消息总线】—— 每天一点小知识

&#x1f4a7; S p r i n g C l o u d R a b b i t M Q 实现分布式消息总线 \color{#FF1493}{Spring Cloud RabbitMQ 实现分布式消息总线} SpringCloudRabbitMQ实现分布式消息总线&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; …

redis集群原理

redis是单线程&#xff0c;但是一般的作为缓存使用的话&#xff0c;redis足够了&#xff0c;因为它的读写速度太快了。 官方的一个简单测试&#xff1a; 测试完成了50个并发执行100000个请求。 设置和获取的值是一个256字节字符串。 结果:读的速度是110000次/s,写的速度是81000…

基于JAVA的高校心理咨询系统/ 基于web的高校心理咨询系统/心理咨询系统

​ 摘 要 近年来,基于网络的高校心理咨询系统已成为社会的发展趋势&#xff0c;我国对高校心理咨询非常重视&#xff0c; 高校心理咨询工作面临着严峻考验&#xff0c;而网络的普及为高校心理咨询的信息化提供了必要的条件。本文主要研究了如何设计和开发基于B/S 高校心理咨询…

[进阶]反射-认识反射、获取类

反射 反射就是&#xff1a;加载类&#xff0c;并允许以编程的方式解剖类中的各种成分&#xff08;成员变量、方法、构造器等&#xff09;。 反射学什么&#xff1f; 学习获取类的信息、操作它们 反射第一步&#xff1a;加载类&#xff0c;获取类的字节码&#xff1a;Class对…

十大基础算法

一、选择排序 过程简单描述&#xff1a; 首先&#xff0c;找到数组中最小的那个元素&#xff0c;其次&#xff0c;将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。其次&#xff0c;在剩下的元素中找到最小的元素&#xff0c;将它与数组的第二…

翻筋斗觅食策略改进海鸥优化算法

目录 一、非线性参数A策略 二、翻筋斗觅食策略 基本 SOA 有一些局限性&#xff0c;例如容易陷入局部最优值、后期收敛缓慢、勘探与开发能力不平衡等&#xff0c;在求解复杂优化问题时尤甚。为了改善基本SOA的不足&#xff0c;提出一种基于翻筋斗觅食策略的改进 SOA(SFSOA)。…

【Neo4j教程之CQL函数基本使用】

&#x1f680; Neo4j &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;C…

基于stm32单片机的ESP-01S模块(wifi模块)的使用

一、开发环境 开发板&#xff1a;野火stm32f407开发板 wifi模块&#xff1a;ESP-01S&#xff08;核心处理器 ESP8266 &#xff09;&#xff0c;固件版本&#xff08;MQTT固件&#xff09; 操作系统&#xff1a;rt-thread操作系统 二、ESP-01S模块介绍 ESP-01S 是由安信可科技开…

【已解决】Vue3使用Element-plus按需加载时消息弹框ElMessage没有样式

Vue3使用Element-plus时消息弹框ElMessage没有样式 问题描述 Element-plus在使用ElMessage消息弹框的时候没有样式&#xff0c;按照官方的按需加载的方式引入的 1、Element-plus使用了自动按需导入&#xff0c;vite.config.js配置如下&#xff1a; plugins: [vue(),AutoImpo…

【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(通用语言体系)

一文带领你透视DDD领域驱动模型的本质和设计原理分析指南&#xff08;通用语言体系&#xff09; 前言介绍降低沟通成本问题&#xff1a;各自建立属于自己领域/层面的语言标准解决方案1&#xff1a;专业术语转换为接地气的话术解决方案2&#xff1a;用简单的案例和背景信息进行描…

【无监督学习】1、MOCOv1 | 用于提升无监督学习效果的动量对比学习

文章目录 一、背景二、方法2.1 对比学习&#xff08;字典查表&#xff09;2.2 动量对比函数2.3 Pretext Task 三、效果3.1 数据集3.2 训练细节3.3 实验 论文&#xff1a;Momentum Contrast for Unsupervised Visual Representation Learning 代码&#xff1a;https://github.c…

【Java|多线程与高并发】wait和notify方法详解

文章目录 1.前言2.wait和notify的基本使用3. notifyAll方法4. wait和sleep方法的对比5. 总结 1.前言 在Java多线程环境中,线程之间是抢占式执行的,线程的调度是随机的.这就很难受了. 在很多情况下我们希望线程以我们想要的顺序来执行. 这就需要wait和notify这两个方法 2.wait和…

Java——《面试题——MySQL篇》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 目录 前文 1、数据库的三范式是什么&#xff1f; 2、MySQL数据库引擎有哪些 3、说说…

【五子棋实战】第2章 博弈树负值极大alpha-beta剪枝算法

【五子棋实战】第2章 博弈树极大极小值alpha-beta剪枝算法 博弈树 极大极小值搜索Minimax 负值极大法Negamax alpha-beta剪枝 博弈树负值极大alpha-beta剪枝算法代码实现 ## 初始化输入、确定输出 ## 开始回溯 ## 判赢 ## 评估-计算分数 总结 继续学习下一篇实战&…

注解和异常的详细笔记

注解的理解 注解(Annotation)也被称为元数据(Metadata)&#xff0c;用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。和注释一样&#xff0c;注解不影响程序逻辑&#xff0c;但注解可以被编译或运行&#xff0c;相当于嵌入在代码中的补充信息。在 JavaSE 中&am…