当前位置: 首页 > news >正文

【IC】NoC设计入门 -- 网络接口NI Slave

我们终于来到了“拼图”的最后一块,也是最精妙的一块:设计“收货方”,即“Slave NI” (SNI)
(在一些文献中,它也被称为“Target NI”或TNI)。

这是一个了不起的时刻。建造完这个模块后,我们就拥有了端到端 (End-to-End) 发送数据所需的全套“积木”!

1. 🎯 “收货方” (Slave NI) 的挑战

这个模块是master_ni的“镜像”。它的工作是:

  1. 扮演“NoC Slave”:在NoC一侧,它“被动地”接收来自router的Flits。
  2. 扮演“AXI Master”:在AXI一侧,它**“主动地”**扮演一个“CPU”(AXI Master),向“下游”的真正“从设备”(如内存控制器)发起一个标准的AXI写操作。

这是它最难的地方: 它必须“接收”一种协议(NoC Flits),然后“重新生成”另一种完全不同的协议(AXI-Master)。

2. 简化与假设(MVP)

为了让这个任务在“教学”上可行,我们必须做出与master_ni (MNI) **“对称”**的简化:

  1. “对称”的包格式:我们的MNI发送 (1 Head + 4 Body + 1 Tail) 的包。因此,我们的SNI必须能**“理解”**这个6-Flit的包。
  2. “对称”的简化:我们只实现“写操作”的接收。
  3. 最重要的“MVP简化” (The “Cheat”)
    • 我们的MNI(发货方)只是把Flit发往一个“坐标” (e.g., (3,3))。它没有在Flit里打包“真正的AXI地址” (e.g., 0xA000_1000)。
    • 因此,我们的SNI(收货方)在收到这个包时,它只知道“有4个数据来了”,但它不知道该把这4个数据写到“内存”的哪个地址
    • 解决方案 (MVP):我们将“硬编码” (Hard-code) 地址。我们规定:所有发送到 (3,3) 路口的SNI的包,都将被固定地写入到内存的 0x4000_0000 地址。
    • (在“v2.0”版本中,MNI会把AWADDR也打成一个Body Flit,SNI会把它存起来——但这会使状态机复杂得多。)

3. “解耦”是关键

我们的SNI会面临“上游”(NoC Router)和“下游”(AXI Memory)的双重“反压” (Backpressure):

  1. NoC router 可能会以“背靠背”的速度(m_noc_ready=1时)连续发来6个Flits。
  2. 但“下游”的AXI memory 可能很“慢”(s_axi_wready=0)。

我们不能在“接收”Flit的同时“发送”AXI WDATA,因为memory可能会“卡住”我们,导致我们无法接收router发来的下一个Flit,进而导致router的FIFO变满,造成NoC网络拥堵

解决方案: 和MNI一样,我们必须在SNI内部放一个**“解耦FIFO”**。

SNI的核心工作流(两阶段):

  1. 阶段1 (接收): 从NoC“收货”,把 Body Flits(货物)全部存入内部的FIFO“缓存区”。
  2. 阶段2 (发送): 当“收货”完毕(收到Tail Flit),再“转身”扮演AXI Master,**“慢慢地”**从内部FIFO中取出数据,按照AXI协议发送给内存。

4. 📐 SNI 的“蓝图” (Module Interface)

/** 模块: 最小可行 Slave NI (MVP Slave Network Interface)* 功能: NoC Flits -> AXI4-Master (写操作)* 假设: - 只接收写操作* - 接收 (1 Head + 4 Body + 1 Tail) 包* - AXI地址是“硬编码”的*/
module slave_ni #(parameter FLIT_WIDTH  = 32,parameter COORD_WIDTH = 4,parameter DATA_BEATS  = 4,parameter FIXED_AXI_ADDR = 32'h4000_0000 // "MVP简化"的硬编码地址
) (input clk,input rst_n,// ---- NoC Slave 接口 (来自 Router) ----input [FLIT_WIDTH-1:0] s_noc_flit,  // Router 发来的Flitinput                  s_noc_valid, // Router 的Flit有效output reg             m_noc_ready, // "SNI: 我准备好了"// ---- AXI4-Master 接口 (去往 Memory) ----// (我们是 Master, 所以 'm_axi_' 是 output)// -- AW (Address Write) Channel --output reg [31:0]           m_axi_awaddr,  // "SNI: 我要写这个地址"output reg                  m_axi_awvalid, // "SNI: 我的地址有效"input                       s_axi_awready, // "Memory: 地址我收到了"// -- W (Write Data) Channel --output reg [FLIT_WIDTH-1:0] m_axi_wdata,   // "SNI: 这是数据"output reg                  m_axi_wlast,   // "SNI: 这是最后一个数据"output reg                  m_axi_wvalid,  // "SNI: 我的数据有效"input                       s_axi_wready,  // "Memory: 数据我收到了"// -- B (Write Response) Channel --input                       s_axi_bvalid,  // "Memory: 写入成功"output reg                  m_axi_bready   // "SNI: 我收到'成功'了"
);// TODO: 核心状态机endmodule

5. 🧠 核心逻辑:“两阶段”状态机

我们将用一个状态机来管理“接收”和“发送”两个阶段。

    // ---------------------------------------------// 内部“解耦FIFO” (我们的“收货暂存区”)// ---------------------------------------------wire [FLIT_WIDTH-1:0] w_fifo_data_out;wire                  w_fifo_empty;wire                  w_fifo_full;wire                  w_fifo_write_en; // FSM 控制“何时写FIFO”wire                  w_fifo_read_en;  // FSM 控制“何时读FIFO”simple_fifo #(.FLIT_WIDTH(FLIT_WIDTH),.FIFO_DEPTH(DATA_BEATS) // 深度 4) w_data_buffer (.clk        (clk),.rst_n      (rst_n),.i_write_en (w_fifo_write_en),.i_data     (s_noc_flit), // 写入的数据 = NoC来的Flit.o_full     (w_fifo_full),.i_read_en  (w_fifo_read_en),.o_data     (w_fifo_data_out),.o_empty    (w_fifo_empty));// ---------------------------------------------// 状态机定义// ---------------------------------------------// 阶段 1: 接收 NoC 包localparam S_IDLE        = 4'd0; // 0. 等待 Head Flitlocalparam S_RECV_BODY   = 4'd1; // 1. 接收 Body Flits (存入FIFO)localparam S_RECV_TAIL   = 4'd2; // 2. 接收 Tail Flit (包已收齐)// 阶段 2: 发送 AXI 事务localparam S_SEND_AW     = 4'd3; // 3. 发送 AXI 地址localparam S_SEND_WDATA  = 4'd4; // 4. 发送 AXI 数据 (从FIFO读)localparam S_RECV_BRESP  = 4'd5; // 5. 等待 AXI 响应reg [3:0] state, next_state;reg [1:0] data_counter; // 0-3 (用于 4-beat)// ---------------------------------------------// 状态机 (时序逻辑)// ---------------------------------------------always @(posedge clk or negedge rst_n) beginif (!rst_n) beginstate <= S_IDLE;end else beginstate <= next_state;endend// ---------------------------------------------// 状态机 (组合逻辑:状态转移 + 输出)// ---------------------------------------------always @(*) begin// --- 默认输出 (防止Latch) ---next_state       = state;m_noc_ready      = 1'b0; // 默认“不准备好”w_fifo_write_en  = 1'b0;w_fifo_read_en   = 1'b0;m_axi_awaddr     = FIXED_AXI_ADDR; // 地址总是固定的m_axi_awvalid    = 1'b0;m_axi_wdata      = w_fifo_data_out; // 数据 = FIFO的输出m_axi_wvalid     = 1'b0;m_axi_wlast      = 1'b0;m_axi_bready     = 1'b0;// --- 主状态机 ---case (state)// 0. 等待 Head FlitS_IDLE: beginm_noc_ready = 1'b1; // "我准备好接收新包"if (s_noc_valid) begin // 收到 (Head)next_state = S_RECV_BODY;data_counter = 0;endend// 1. 接收 Body Flits (存入FIFO)S_RECV_BODY: begin// "我准备好" = "我的FIFO没满" (关键的解耦/反压)m_noc_ready = !w_fifo_full;// "写入FIFO" = "Router有效" AND "我准备好"w_fifo_write_en = s_noc_valid && m_noc_ready;if (w_fifo_write_en) beginif (data_counter == DATA_BEATS - 1) begin // 收齐4个了?next_state = S_RECV_TAIL;end else begindata_counter = data_counter + 1;endendend// 2. 接收 Tail Flit (包已收齐)S_RECV_TAIL: beginm_noc_ready = 1'b1; // "我准备好接收Tail"if (s_noc_valid) begin// "收货"阶段结束!next_state = S_SEND_AW; // "转身", 开始"发货"endend// 3. 发送 AXI 地址 (阶段2开始)S_SEND_AW: beginm_axi_awvalid = 1'b1; // "我的地址有效"if (s_axi_awready) begin // "Memory 确认收到?"next_state = S_SEND_WDATA;data_counter = 0;endend// 4. 发送 AXI 数据 (从FIFO读)S_SEND_WDATA: begin// "我的数据有效" = "我的FIFO不为空"m_axi_wvalid = !w_fifo_empty;// "从FIFO读" = "我数据有效" AND "Memory准备好"w_fifo_read_en = m_axi_wvalid && s_axi_wready;if (w_fifo_read_en) begin // 成功发送1拍if (data_counter == DATA_BEATS - 1) begin // 是最后一拍吗?m_axi_wlast = 1'b1;next_state = S_RECV_BRESP;end else begindata_counter = data_counter + 1;endendend// 5. 等待 AXI 响应S_RECV_BRESP: beginm_axi_bready = 1'b1; // "我准备好接收'成功'信号"if (s_axi_bvalid) begin // "Memory 确认了"next_state = S_IDLE; // "一个完整的事务结束, 返回空闲"endenddefault: beginnext_state = S_IDLE;endendcaseendendmodule

🏁 最终总结:我们做到了!

我们成功了!

你已经亲手设计了NoC世界中**“发货方”“收货方”**的完整“物流链”。

你现在拥有了实现一个**完整SoC(片上系统)**所需的全套“积木”:

  1. router.v:“智能十字路口”,负责“路由”。
  2. master_ni.v:“发货方”(连接CPU),负责“打包”(AXI -> Flit)。
  3. slave_ni.v:“收货方”(连接Memory),负责“拆包”(Flit -> AXI)。

你已经远远超越了“理论”,你亲手解决了真实世界中“协议转换”和“时钟域/速率解耦”(通过FIFO)的核心工程难题。


http://www.dtcms.com/a/570525.html

相关文章:

  • 山东淄博网站建设的公司python做笔记的网站
  • cf div2 1061个人补题笔记
  • 衡阳市做网站免费网站站长推广
  • 【C++闯关笔记】unordered_map与unordered_set的底层:哈希表(哈希桶)
  • 项目部署方法总结
  • 注册网站会员需要填写信息工程设计有限公司
  • 建设网站全部流程个人网站建设制作
  • 用php做网站的方法网站开发团队分工
  • 网站规划中的三种常用类型学习网
  • app企业网站模板贵阳网站制作专业
  • 提出网络营销思想的网站改版计划腰椎间盘突出压迫神经腿疼怎么治疗
  • ref 和 reactive的区别与用法
  • 网站整套模板做网站哪个平台
  • asp与sql做网站莱州网站建设多少钱
  • UE C++ 代码上构建反射
  • 360建筑网官方网站网站运营编辑
  • 网站点赞怎么做邮箱域名和网站域名
  • 企业采购如何管理部门预算?
  • 三、ILA逻辑分析仪抓取及查看波形
  • asp.net网站本机访问慢网络运维需要懂什么技术
  • 网站的相关性 实用性网站建设项目登记表
  • 深圳本地做网站wordpress 文章列表只显示标题
  • notion模版 | 小胡的第二大脑[特殊字符]-任务篇
  • harmonyos的鸿蒙的跳转页面的部署
  • Godaddy优惠码网站怎么做的红豆梧州论坛
  • 商户查询缓存、商户更新缓存(opsForHash、opsForList、ObjectMapper、@Transactional、@PutMapping、RequestParam、装箱拆箱、线程池)
  • 做网站如何推销网站建设论证方案
  • 济南企业网站推广网络销售的工作内容
  • 大神自己做的下载音乐的网站域名是什么意思举个例子
  • Python中常用内置函数下【含代码理解】