实现NoC的RTL设计一直是很复杂的,而NoC生成器(Network-on-Chip Generator)是一种用于设计和生成Network-on-Chip架构的工具。它可以根据用户的需求和参数,如网络的尺寸、拓扑结构、路由算法等,自动生成具有所需特性的NoC设计。主要功能包括自动生成和优化NoC设计以及进行性能分析和验证。简而言之,NoC生成器可以帮助实现更高效且可靠的系统集成设计。
ProNoC有一个很简洁的GUI界面。
除了配置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)为测试提供激励。
按照官网用户手册的 《CHAPTER 5. Processing Tile Generator Hello World Tutorial》生成单个Processing Tile以用来搭建多核网络。
cd ~/ProNoC/mpsoc/perl_gui
perl ./ProNoC.pl
进去以后,在上侧的菜单栏选择“Processing tile generator”。
在左侧的树视图窗口中,您可以看到所有可用 IP 类别的列表。 单击每个类别展开相关的 IP 核列表。 每个 IP 核都可以通过双击其名称添加到 GUI。
IO
==。 在设置IP的时候可以看到,参数的选项有三个:localparameter
和parameter
。
localparam
和parameter
是Verilog硬件描述语言中的两个关键字,它们都用于定义参数,但在用法和应用场景上有所不同。
parameter
是Verilog中的常数,一般用于模块间传参,即允许在实例化模块的时候设置参数的值。这就意味着,如果你选择把它定义为parameter
,那么在网络-on-chip(NoC)基础上的MPSoC生成时,你就可以更改处理tile参数的值,允许在不同地方以不同的参数值调用相同的tile。
与此相反,localparam
定义的是局部参数,这些参数不能从模块外部更改。一旦在模块内部被定义,它们的值就固定了,无法在模块实例化时更改。
所以,何时使用localparam
和parameter
主要取决于你的需求:
parameter
。localparam
。关于"不包括"(Don’t include)的选择,如果参数是一个需要在软件代码中使用的软件参数,你可以选择这个选项。这样,参数将不会在生成的Verilog文件中被定义,而是在软件代码中定义和使用。
双击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;
按照下图设置:图片里的名字没有完全对上,比如clk_source:clk;
官方文档图里标黄的地方的slave的号没对上,我这里设置了:
slave[0]连RAM
slave[1]连NI
slave[2]连Timer
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;
值得注意的是,
请注意,本章中所需的磁贴具有要连接到 NoC 的网络接口 (NI) IP。
添加所有需要的 IP 内核后,现在您可以通过单击 Wishbone-bus addr
按钮是==WB addr
==检查自动分配的 Wishbone 总线地址。这些地址是根据 IP 内核库设置、插入的参数和系统中重复的相同 IP 内核的数量自动设置的。 但是,您可以随意将它们调整为新值,因为插入的地址没有冲突。
我使用了默认的值。
ps, instance name前的那个序号0:uart的序号顺序好像没什么影响。
点击 Diagram 按钮观察 SoC 功能框图,上面有三个选项:移除未连接的实例、移除clk的连接、移除reset的连接。可以得到下图的连接关系,很方便。
如果计划将处理块用于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 内的内部模块,则可以忽略时钟设置。
报错记录:我在使用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的窗口和相关的资源会被自动销毁。如果你在这之后尝试访问这些资源(比如,调用一个方法或者获取一些属性),你会遇到这个错误,因为你正在试图操作一个不存在的窗口。
尝试解决方法:
设置Tile名称为==mor1k_tile
==。
按Generate RTL
按钮生成这个Tile的RTL代码。
至此,一个Tile已经搭建完毕了。接下来尝试搭建2x2的NoC网络。
在官网用户手册第七章《CHAPTER 7. Simple message passing demo on 2×2 MPSoC》
按照下图,使用我们做好的这个Tile搭建NoC网络。
重新启动ProNoC用于生成Tile的GUI界面。进去以后,在上侧的菜单栏选择“NoC based MPSoC generator”。
cd ~/ProNoC/mpsoc/perl_gui
perl ./ProNoC.pl
按照表 7.2 设置 NoC 配置设置。 在这里,我们定义了两个虚拟网络 (VN, Virtual Network),方法是定义两个消息类别并分离消息类别允许的 VC,每个消息类别只能使用其自己的专用 VC。
在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 按钮,它会显示一个窗口,您可以在其中更改默认参数值。具有自定义参数设置的磁贴在其名称上标有 *。
点击Diagram
按钮查看结构框图如下:
还可以查看NoC网络的参数
同1.7所讲
如果应使用 FPGA clk 引脚生成 MPSoC clk,请单击 CLK
设置按钮然后跟随 CLK 设置以生成 MPSoC 时钟。
Click on Generate RTL
button to generate the MPSoC RTL code.
点击software
进行软件编程使用C代码
在左边的Tree-View窗口中,可以选择工程sw目录下的任意一个文件打开,然后进行编辑。 用以下 C 代码替换所有图块中 main.c 文件的内容。
在此示例中,tile 0 到 2 将每 3 个数据包发送到 tile 3。 tiles 3 显示串口终端中的数据包内容。
直接复制粘贴到每个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;
}
这个C语言程序是一个多核(MULTI_CORE)并行计算的程序,针对网络接口(Network Interface,简称NI)进行操作。该程序中使用了MOR1K开放源代码RISC(减少指令集计算)处理器的Tile设备驱动程序。处理器的每一个核心(core)被分配了一个ID(COREID)用于标识。
代码的主要部分可以分为几个模块:
pck1
,pck2
和pck3
是需要发送的数据包,receive_buff
是一个二维数组,用于存储每个虚拟通道(VC)接收的数据包。数据包大小被设定为16字节。error_handelling_function
),数据包接收(got_packet_funtion
),数据包检查(check_packet_funtion
)以及数据包发送完成(sent_packet_done_funtion
)等。ni_isr
),当中断发生时,它会调用上述的辅助函数来处理事件。中断可能发生在以下几种情况:接收到新的数据包,数据包保存完成,数据包发送完成,以及出现错误等。pck1
,pck2
和pck3
。最后进入一个空的无限循环。 该代码的主要作用是在多核环境下,利用网络接口进行数据包的发送和接收。其中有一些硬件特定的函数和变量(如COREID
,ni_transfer
,general_cpu_int_en
等),可能需要特定的硬件环境和库才能正确编译和运行。
如果您的系统上安装了 Verilator 软件,您可以在运行您开发的软件时模拟您的 SoC。 为此,请遵循以下说明:
按下右下角的 Compile RTL
按钮。 这应该打开“选择编译器窗口”,如图 5.17 所示。 选择 Verilator 作为编译器工具,然后按Next
。
使用verilator仿真,同时可以把四个核一起仿真。看到如下提示,再点Next
Veriator model has been generated successfully!
这个 Vtile0
类是由 Verilator 从 Verilog 硬件描述语言生成的。通常,这个类会在一个 Verilator 生成的硬件模型中被用到,模型的目标是模拟硬件的行为。具体地,当你有一个使用 Verilog 描述的硬件设计(比如说,一个电路或者一个系统芯片),你可以使用 Verilator 工具将其转换成 C++ 代码。这种转换让你可以在一台普通的电脑上模拟硬件的行为,而不需要任何实际的硬件。
在点完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"可能是一些处理器,它们被连接在一起,形成一个网络。
#include
#include
#include
#include
#include
#include
#include // 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;
}
在此示例中,tile 0 到 2 将每 3 个数据包发送到 tile 3。 tiles 3 显示串口终端中的数据包内容。
得到的输出结果:
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();
update_router_st(unsigned int, unsigned int, unsigned char*)'
在Testbench.cpp里加这个函数,让编译通过。
void update_router_st(unsigned int param1, unsigned int param2, unsigned char* param3) {
// 暂时不做任何事
}
这种报错。请注意,如果您遇到 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嘛,他俩得对应起来。
可以看到生成的一个Tile是基于wishbone总线,使用Mor1kx内核的SoC,RTL代码很符合规范且可以综合,说明可以用RTL进行下一步FPGA板级验证、门级网表综合
把所有报错的地方: `include "pronoc_def.v" 换成 `include "./lib/pronoc_def.v"
把noc_localparam.v的文件名改成noc_localparam.sv再加入到quartus里
再把lib/src_noc/pronoc_pkg.sv里的定义`include "noc_localparam.v"改成`include "noc_localparam.sv"
把这部分第637行改成
genvar i;
generate
for (i=0;i
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)
);
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];
(send_tail)? tail_flit_out : m_send_dat_i [Fpay-1 : 0];
(send_tail)? tail_flit_out : m_send_dat_i [31 : 0];
// 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];
把这一段代码注释掉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.
);
*/
代码Dubug过程和上面一样。可以看到noc_top模块连着四个tile.