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

Verilog实现RPC从机(配合AXI_Slave使用)

本文介绍如何使用 Verilog 实现一个 RPC 从机模块,并通过 AXI-Lite 总线 与外部主机系统(如 Zynq PS 端)进行RPC调用。

具体地,我们在 AXI Slave 模块中实例化 rpc_processor 模块,并将 AXI Slave的寄存器映射到 rpc_processor 的参数/结果接口,使得主机可以通过 AXI 接口:

  • 触发 RPC 调用
  • 传递调用方法与输入参数
  • 接收处理结果与完成标志

该模块需配合我另一篇博客 《Zynq AXI-Lite 总线原理与实现》 一起使用,读者可结合使用 AXI-Lite 接口读写代码与本模块逻辑,实现完整的软硬件交互。

模块功能

rpc_processor模块是RPC从机,外部主机通过AXI Slave接口发送RPC请求,并通过读取AXI Slave寄存器获取处理结果。rpc_processor支持的RPC方法包括:

  • 回显功能(Echo):返回请求的参数。
  • 加法功能(Add): 将请求参数相加并返回结果。

引脚定义

引脚名方向描述位数
i_clk输入时钟信号-
i_rst_n输入复位信号(低有效)-
i_method_reg输入功能码寄存器,选择RPC方法32位
i_req_reg_0输入请求参数032位
i_req_reg_1输入请求参数132位
i_req_reg_2输入请求参数232位
i_req_reg_3输入请求参数332位
o_res_reg_0输出响应结果032位
o_res_reg_1输出响应结果132位
o_res_reg_2输出响应结果232位
o_res_reg_3输出响应结果332位
i_rpc_start输入RPC启动信号(触发处理)1位
o_rpc_valid输出RPC请求有效标志1位
i_rpc_ready输入外部处理就绪信号1位
o_rpc_done输出RPC处理完成标志1位

执行流程:

  • 主机通过写请求传递RPC功能码和参数。
  • rpc_processor模块根据功能码执行操作。
  • 主机通过读请求获取RPC处理结果。

rpc_processor.v

`timescale 1ns/1ps// 宏定义:RPC方法(32位功能码)
`define RPC_FUNC_ECHO    32'h00000000  // 回显功能(返回输入参数)
`define RPC_FUNC_ADD     32'h00000001  // 加法功能(参数相加)module rpc_processor (input  wire        i_clk,         // 时钟信号input  wire        i_rst_n,       // 复位信号(低有效)// 寄存器接口(直接映射到axi_slave寄存器)input  wire [31:0] i_method_reg,  // 方法选择寄存器input  wire [31:0] i_req_reg_0,   // 请求参数0input  wire [31:0] i_req_reg_1,   // 请求参数1input  wire [31:0] i_req_reg_2,   // 请求参数2input  wire [31:0] i_req_reg_3,   // 请求参数3output reg  [31:0] o_res_reg_0,   // 响应结果0output reg  [31:0] o_res_reg_1,   // 响应结果1output reg  [31:0] o_res_reg_2,   // 响应结果2output reg  [31:0] o_res_reg_3,   // 响应结果3// RPC控制信号input  wire        i_rpc_start,   // RPC启动信号(1=触发处理,上升沿有效)output reg         o_rpc_valid,   // RPC请求有效(处理中保持高)input  wire        i_rpc_ready,   // 外部处理就绪(1=可接收请求)output reg         o_rpc_done     // RPC处理完成(1=结果有效)
);// --------------------------// 启动信号边沿检测(防止持续触发)// --------------------------reg r_rpc_start_dly;wire w_rpc_start_posedge;  // 启动信号上升沿(真正的触发点)always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) beginr_rpc_start_dly <= 1'b0;end else beginr_rpc_start_dly <= i_rpc_start;  // 延迟一拍用于边沿检测endendassign w_rpc_start_posedge = i_rpc_start && !r_rpc_start_dly;  // 上升沿检测// --------------------------// 内部锁存寄存器(处理期间保持参数稳定)// --------------------------reg [31:0] r_method_latch;reg [31:0] r_req_latch_0, r_req_latch_1, r_req_latch_2, r_req_latch_3;// --------------------------// RPC处理状态机// --------------------------localparam S_IDLE      = 2'b00;localparam S_PROCESSING = 2'b01;localparam S_DONE      = 2'b10;reg [1:0] r_state;reg [3:0] r_proc_cnt;  // 模拟处理延迟(0~15周期)always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) beginr_state <= S_IDLE;r_proc_cnt <= 4'h0;o_rpc_valid <= 1'b0;o_rpc_done <= 1'b0;r_method_latch <= 32'h0;r_req_latch_0 <= 32'h0;r_req_latch_1 <= 32'h0;r_req_latch_2 <= 32'h0;r_req_latch_3 <= 32'h0;o_res_reg_0 <= 32'h0;o_res_reg_1 <= 32'h0;o_res_reg_2 <= 32'h0;o_res_reg_3 <= 32'h0;end else begincase (r_state)S_IDLE: begino_rpc_done <= 1'b0;  // 空闲状态下完成标志清0// 检测到启动信号上升沿,且外部就绪时,启动处理if (w_rpc_start_posedge && i_rpc_ready) begin// 锁存当前寄存器值(处理期间参数不变)r_method_latch <= i_method_reg;r_req_latch_0 <= i_req_reg_0;r_req_latch_1 <= i_req_reg_1;r_req_latch_2 <= i_req_reg_2;r_req_latch_3 <= i_req_reg_3;o_rpc_valid <= 1'b1;      // 置位请求有效r_state <= S_PROCESSING;    // 进入处理状态r_proc_cnt <= 4'h0;       // 重置延迟计数器end else begino_rpc_valid <= 1'b0;r_state <= S_IDLE;endendS_PROCESSING: begin// 模拟处理延迟(例如10个时钟周期,可修改)if (r_proc_cnt >= 4'd9) begin// 根据方法号执行不同处理(示例逻辑)case (r_method_latch)`RPC_FUNC_ECHO: begin  // 方法0:返回请求参数o_res_reg_0 <= r_req_latch_0;o_res_reg_1 <= r_req_latch_1;o_res_reg_2 <= r_req_latch_2;o_res_reg_3 <= r_req_latch_3;end`RPC_FUNC_ADD: begin  // 方法1:参数相加o_res_reg_0 <= r_req_latch_0 + r_req_latch_1;o_res_reg_1 <= r_req_latch_2 + r_req_latch_3;o_res_reg_2 <= r_req_latch_0 + r_req_latch_2;o_res_reg_3 <= r_req_latch_1 + r_req_latch_3;enddefault: begin  o_res_reg_0 <= 32'h0;o_res_reg_1 <= 32'h0;o_res_reg_2 <= 32'h0;o_res_reg_3 <= 32'h0;endendcaser_state <= S_DONE;end else beginr_proc_cnt <= r_proc_cnt + 1'b1;r_state <= S_PROCESSING;endendS_DONE: begino_rpc_valid <= 1'b0;      // 清除请求有效o_rpc_done <= 1'b1;       // 置位完成标志(通知结果就绪)r_state <= S_IDLE;          // 返回空闲状态,等待下一次启动enddefault: r_state <= S_IDLE;endcaseendendendmodule

tb.v

`timescale 1ns/1ps// 接口定义:抽象DUT的所有信号
interface rpc_if;logic         i_clk;logic         i_rst_n;logic [31:0]  i_method_reg;logic [31:0]  i_req_reg_0;logic [31:0]  i_req_reg_1;logic [31:0]  i_req_reg_2;logic [31:0]  i_req_reg_3;logic         i_rpc_start;logic         i_rpc_ready;logic [31:0]  o_res_reg_0;logic [31:0]  o_res_reg_1;logic [31:0]  o_res_reg_2;logic [31:0]  o_res_reg_3;logic         o_rpc_valid;logic         o_rpc_done;// 时钟生成(50MHz,周期20ns)initial begini_clk = 1'b0;forever #10 i_clk = ~i_clk;end
endinterface// RPC测试类:封装所有测试逻辑
class rpc_tester;// 虚拟接口:用于连接DUTvirtual rpc_if vif;// 宏定义:RPC方法(与DUT保持一致)localparam RPC_FUNC_ECHO = 32'h00000000;localparam RPC_FUNC_ADD  = 32'h00000001;// 构造函数:绑定接口function new(virtual rpc_if ifc);vif = ifc;endfunction// 初始化所有信号task initialize();vif.i_rst_n = 1'b0;vif.i_method_reg = 32'h0;vif.i_req_reg_0 = 32'h0;vif.i_req_reg_1 = 32'h0;vif.i_req_reg_2 = 32'h0;vif.i_req_reg_3 = 32'h0;vif.i_rpc_start = 1'b0;vif.i_rpc_ready = 1'b0;endtask// 执行复位并等待稳定task reset();@(posedge vif.i_clk);vif.i_rst_n = 1'b0;#100;  // 保持复位100ns@(posedge vif.i_clk);vif.i_rst_n = 1'b1;  // 释放复位#20;   // 等待复位释放稳定endtask// 发送RPC请求并等待完成task send_rpc(input logic [31:0] method,input logic [31:0] req0,input logic [31:0] req1,input logic [31:0] req2,input logic [31:0] req3,input logic        ready);// 设置请求参数@(posedge vif.i_clk);vif.i_method_reg = method;vif.i_req_reg_0 = req0;vif.i_req_reg_1 = req1;vif.i_req_reg_2 = req2;vif.i_req_reg_3 = req3;vif.i_rpc_ready = ready;// 产生start上升沿@(posedge vif.i_clk);vif.i_rpc_start = 1'b1;@(posedge vif.i_clk);vif.i_rpc_start = 1'b0;// 等待处理完成(如果ready为1)if (ready) beginwait(vif.o_rpc_done == 1'b1);@(posedge vif.i_clk);endendtask// 验证ADD方法结果task verify_add_result(input logic [31:0] exp0,input logic [31:0] exp1,input logic [31:0] exp2,input logic [31:0] exp3);@(posedge vif.i_clk);if (vif.o_res_reg_0 == exp0 && vif.o_res_reg_1 == exp1 &&vif.o_res_reg_2 == exp2 && vif.o_res_reg_3 == exp3) begin$display("ADD method test passed %d",exp0);end else begin$display("ADD method test failed, Expected: %h %h %h %h, Actual: %h %h %h %h",exp0, exp1, exp2, exp3,vif.o_res_reg_0, vif.o_res_reg_1, vif.o_res_reg_2, vif.o_res_reg_3);endendtask// 运行完整测试序列task run_test();// 初始化并复位initialize();reset();// 执行ADD方法测试send_rpc(RPC_FUNC_ADD,32'h00000001,  // req032'h00000002,  // req132'h00000003,  // req232'h00000004,  // req31'b1           // ready);// 验证结果(1+2=3, 3+4=7, 1+3=4, 2+4=6)verify_add_result(32'h00000003,32'h00000007,32'h00000004,32'h00000006);// 测试完成#100;$display("All tests completed");$finish;endtask
endclass// 顶层测试平台
module tb;wire w_test;// 实例化接口rpc_if rpc_ifc();// 实例化DUT并连接接口rpc_processor uut (.i_clk         (rpc_ifc.i_clk),.i_rst_n       (rpc_ifc.i_rst_n),.i_method_reg  (rpc_ifc.i_method_reg),.i_req_reg_0   (rpc_ifc.i_req_reg_0),.i_req_reg_1   (rpc_ifc.i_req_reg_1),.i_req_reg_2   (rpc_ifc.i_req_reg_2),.i_req_reg_3   (rpc_ifc.i_req_reg_3),.o_res_reg_0   (rpc_ifc.o_res_reg_0),.o_res_reg_1   (rpc_ifc.o_res_reg_1),.o_res_reg_2   (rpc_ifc.o_res_reg_2),.o_res_reg_3   (rpc_ifc.o_res_reg_3),.i_rpc_start   (rpc_ifc.i_rpc_start),.o_rpc_valid   (rpc_ifc.o_rpc_valid),.i_rpc_ready   (rpc_ifc.i_rpc_ready),.o_rpc_done    (rpc_ifc.o_rpc_done));// 启动测试initial begin// 创建测试实例并运行测试rpc_tester tester = new(rpc_ifc);tester.run_test();end
endmodule

测试结果

因为
32'h00000001,  // req0
32'h00000002,  // req1
所以测试结果为
ADD method test passed          3
http://www.lryc.cn/news/605684.html

相关文章:

  • 金融专题|某跨境支付机构:以榫卯企业云平台 VPC 功能保障业务主体安全
  • 查询目前服务器所占的带宽的命令(上传和下载)
  • TTS语音合成|f5-tts语音合成服务器部署,实现http访问
  • 【Kiro Code 从入门到精通】重要的功能
  • 安全月报 | 傲盾DDoS攻击防御2025年7月简报
  • python中高效构建提示词
  • 关于PHP学习
  • 【BUG】nvm无法安装低版本Node.js:The system cannot find the file specified解决方案
  • iOS15及以后国际化如何设置.xcstrings文件默认语言
  • Jmeter全局变量跨线程组的使用
  • ShimetaPi M4-R1:国产高性能嵌入式平台的异构计算架构与OpenHarmony生态实践
  • Video Pixel Repetition
  • Spring AI MCP 技术从使用到项目实战深度解析
  • 数据结构:多项式加法(Polynomial Addition)
  • Linux多线程线程控制
  • PHP开发
  • 《质光相济:Three.js中3D视觉的底层交互逻辑》
  • Redis高频问题全解析
  • 深度理解 linux 系统内存分配
  • [特殊字符] 数字孪生 + 数据可视化:实战经验分享,让物理世界数据 “会说话”
  • Java【代码 21】将word、excel文件转换为pdf格式和将pdf文档转换为image格式工具类分享(Gitee源码)aspose转换中文乱码问题处理
  • ubuntu24.04环境下树莓派Pico C/C++ SDK开发环境折腾记录
  • STM32学习记录--Day4
  • 云原生运维与混合云运维:如何选择及 Wisdom SSH 的应用
  • AI编程新工具!使用 LangGraph 构建复杂工作流
  • Cesium 快速入门(七)材质详解
  • 数据结构 ArrayList与顺序表
  • 计算机网络学习(一、Cisco Packet Tracer软件安装)
  • Redis线程模型讨论
  • 无人机飞控系统3D (C++)实践