基于 AXI-Lite 实现可扩展的硬件函数 RPC 框架(附完整源码)

AXI-Lite 实现RPC调用硬件函数服务

👋 本文介绍如何基于 AXI-Lite 总线设计一个通用的“硬件函数调用框架”。主机端(PS)只需通过寄存器写入参数与启动标志,即可触发 PL 模块执行指定算法逻辑,并将结果返回。

该机制本质上是一种硬件层的远程过程调用(RPC),在嵌入式/FPGA 系统中具有广泛应用价值,特别适用于:

  • 💡 自定义算法逻辑(如加法、乘法、查表)
  • ⚡ 硬件加速模块(如滤波、压缩、比对)
  • 🧱 基于寄存器映射的服务型模块自定义外设

AXI-Lite的原理和使用可参考我的另一篇文章:

👉 《Zynq AXI-Lite 总线原理与实现》


BD图

在这里插入图片描述


📦 系统结构

     ┌──────────────┐│   PS 主机    ││  (ARM/Linux) │└──────┬───────┘│AXI-Lite┌──────▼───────┐│ axi_lite_slave │ ← 提供寄存器读写接口└──────┬───────┘│┌──────▼──────────┐│  rpc_processor │ ← 执行“函数”调用(如加法)└────────────────┘

🧠 功能简介

  • ✅ PS 主机通过 AXI-Lite 写入参数、选择方法、触发执行
  • ✅ PL 模块 rpc_processor 根据方法编号执行对应处理(如参数加法)
  • ✅ 支持多个返回值(最多 4 个)
  • ✅ 通过仿真 testbench + PS 代码两种方式调用与验证

📐 寄存器定义

地址偏移名称作用位说明
0x00REG_CTRL控制与状态寄存器[0]=start,[1]=ready,[2]=done,[3]=valid
0x04REG_METHOD方法编号(功能码)0 = 回显(ECHO),1 = 加法(ADD)
0x08~0x14REG_ARG0~3输入参数(共4个)32-bit 输入
0x18~0x24REG_RES0~3输出结果(共4个)32-bit 输出

📌 所有寄存器均为 32-bit,支持通过 AXI-Lite 接口进行读写访问。


寄存器映射表

mem索引地址偏移寄存器名称说明
mem[0]0x00REG_CTRL控制/状态
mem[1]0x04REG_METHOD方法编号
mem[2]0x08REG_ARG0请求参数0
mem[3]0x0CREG_ARG1请求参数1
mem[4]0x10REG_ARG2请求参数2
mem[5]0x14REG_ARG3请求参数3
mem[6]0x18REG_RES0响应结果0
mem[7]0x1CREG_RES1响应结果1
mem[8]0x20REG_RES2响应结果2
mem[9]0x24REG_RES3响应结果3

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,       // 复位信号(低有效)// 寄存器接口(直接暴露)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: begin// 检测到启动信号上升沿,且外部就绪时,启动处理if (w_rpc_start_posedge && i_rpc_ready) begino_rpc_done <= 1'b0;  // 完成标志清0// 锁存当前寄存器值(处理期间参数不变)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_rpc_processor.sv

`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);// 执行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

axi_lite_slave.v

module axi_lite_slave #(parameter ADDR_WIDTH = 32,             // 地址总线位宽 Address widthparameter DATA_WIDTH = 32,             // 数据总线位宽 Data widthparameter MEM_SIZE   = 1024            // 内部存储器大小(单位:字节) Memory size in bytes
)(input  wire                     clk,           // 时钟 Clockinput  wire                     rst_n,         // 异步复位,低有效 Asynchronous reset, active-low// AXI 写地址通道(Write Address Channel)input  wire [ADDR_WIDTH-1:0]    s_axi_awaddr,  // 写地址 Write addressinput  wire                     s_axi_awvalid, // 写地址有效 Write address validoutput reg                      s_axi_awready, // 写地址就绪 Write address ready// AXI 写数据通道(Write Data Channel)input  wire [DATA_WIDTH-1:0]    s_axi_wdata,   // 写数据 Write datainput  wire [(DATA_WIDTH/8)-1:0] s_axi_wstrb,  // 写字节使能 Byte enableinput  wire                     s_axi_wvalid,  // 写数据有效 Write data validoutput reg                      s_axi_wready,  // 写数据就绪 Write data ready// AXI 写响应通道(Write Response Channel)output reg  [1:0]               s_axi_bresp,   // 写响应 Write responseoutput reg                      s_axi_bvalid,  // 写响应有效 Write response validinput  wire                     s_axi_bready,  // 写响应就绪 Write response ready// AXI 读地址通道(Read Address Channel)input  wire [ADDR_WIDTH-1:0]    s_axi_araddr,  // 读地址 Read addressinput  wire                     s_axi_arvalid, // 读地址有效 Read address validoutput reg                      s_axi_arready, // 读地址就绪 Read address ready// AXI 读数据通道(Read Data Channel)output reg  [DATA_WIDTH-1:0]    s_axi_rdata,   // 读数据 Read dataoutput reg  [1:0]               s_axi_rresp,   // 读响应 Read responseoutput reg                      s_axi_rvalid,  // 读数据有效 Read data validinput  wire                     s_axi_rready   // 读数据就绪 Read data ready
);// 内部寄存器(寄存器文件,32-bit对齐)reg [DATA_WIDTH-1:0] mem [0:(MEM_SIZE/DATA_WIDTH)-1];// RPC 处理器输出端口连接线wire [31:0] w_res_reg_0, w_res_reg_1, w_res_reg_2, w_res_reg_3;wire        w_rpc_done, w_rpc_valid;// 状态定义(State definitions)localparam IDLE        = 3'd0;localparam WRITE_ADDR  = 3'd1;localparam WRITE_DATA  = 3'd2;localparam WRITE_RESP  = 3'd3;localparam READ_ADDR   = 3'd4;localparam READ_DATA   = 3'd5;reg [2:0] state, next_state;              // 状态寄存器reg [ADDR_WIDTH-1:0] write_addr;         // 写地址寄存器reg [ADDR_WIDTH-1:0] read_addr;          // 读地址寄存器// 状态机:状态跳转逻辑always @(posedge clk or negedge rst_n) beginif (!rst_n)state <= IDLE;elsestate <= next_state;end// 状态机:下一状态逻辑always @(*) beginnext_state = state;case (state)IDLE: beginif (s_axi_awvalid)next_state = WRITE_ADDR;else if (s_axi_arvalid)next_state = READ_ADDR;endWRITE_ADDR: next_state = WRITE_DATA;WRITE_DATA: if (s_axi_wvalid) next_state = WRITE_RESP;WRITE_RESP: if (s_axi_bready) next_state = IDLE;READ_ADDR : next_state = READ_DATA;READ_DATA : if (s_axi_rready) next_state = IDLE;default   : next_state = IDLE;endcaseend// 输出逻辑:AXI 信号 + 寄存器读写always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 所有输出复位s_axi_awready <= 0;s_axi_wready  <= 0;s_axi_bresp   <= 0;s_axi_bvalid  <= 0;s_axi_arready <= 0;s_axi_rdata   <= 0;s_axi_rresp   <= 0;s_axi_rvalid  <= 0;write_addr    <= 0;read_addr     <= 0;//mem 初始化mem[0][0] <= 1'b0;mem[0][2] <= 1'b0;mem[0][3] <= 1'b0;mem[6]    <= 32'd0;mem[7]    <= 32'd0;mem[8]    <= 32'd0;mem[9]    <= 32'd0;// 模块重编译生效测试mem[20]   <= 7624;end else begincase (state)IDLE: begin// 清除所有 ready/valid 信号s_axi_awready <= 0;s_axi_wready  <= 0;s_axi_bvalid  <= 0;s_axi_arready <= 0;s_axi_rvalid  <= 0;// 接收写地址或读地址if (s_axi_awvalid) begins_axi_awready <= 1;write_addr    <= s_axi_awaddr;end else if (s_axi_arvalid) begins_axi_arready <= 1;read_addr     <= s_axi_araddr;endendWRITE_ADDR: begins_axi_awready <= 0;s_axi_wready  <= 1;   // 等待写数据endWRITE_DATA: beginif (s_axi_wvalid) begins_axi_wready <= 0;mem[write_addr[8:2]] <= s_axi_wdata;  // 写入数据s_axi_bresp  <= 2'b00;                // OKAYs_axi_bvalid <= 1;endendWRITE_RESP: beginif (s_axi_bready)s_axi_bvalid <= 0;endREAD_ADDR: begins_axi_arready <= 0;s_axi_rdata   <= mem[read_addr[8:2]];     // 读取数据s_axi_rresp   <= 2'b00;                   // OKAYs_axi_rvalid  <= 1;endREAD_DATA: beginif (s_axi_rready)s_axi_rvalid <= 0;endendcase// 每个时钟周期更新 RPC 输出值到寄存器(6~9)mem[6] <= w_res_reg_0;mem[7] <= w_res_reg_1;mem[8] <= w_res_reg_2;mem[9] <= w_res_reg_3;mem[0][2] <= w_rpc_done;mem[0][3] <= w_rpc_valid;endend// 实例化 RPC 处理器模块,连接输入参数和输出结果寄存器rpc_processor u_rpc (.i_clk        (clk),.i_rst_n      (rst_n),.i_method_reg (mem[1]),        // 功能号寄存器.i_req_reg_0  (mem[2]),        // 参数0.i_req_reg_1  (mem[3]),        // 参数1.i_req_reg_2  (mem[4]),        // 参数2.i_req_reg_3  (mem[5]),        // 参数3.o_res_reg_0  (w_res_reg_0),   // 返回值0.o_res_reg_1  (w_res_reg_1),   // 返回值1.o_res_reg_2  (w_res_reg_2),   // 返回值2.o_res_reg_3  (w_res_reg_3),   // 返回值3.i_rpc_start  (mem[0][0]),     // 启动标志.i_rpc_ready  (mem[0][1]),     // RPC主机方法和参数准备好了.o_rpc_done   (w_rpc_done),    // RPC处理完成(1=结果有效).o_rpc_valid  (w_rpc_valid)    // RPC请求有效(处理中保持高));endmodule

PS 裸机测试

#include "xil_io.h"
#include "xil_printf.h"
#include <stdio.h>#define BASE_ADDR       0x43c00000
#define MAX_INDEX   30
#define REG_CTRL        (BASE_ADDR + 4*0)
#define REG_METHOD      (BASE_ADDR + 4*1)
#define REG_ARG0        (BASE_ADDR + 4*2)
#define REG_ARG1        (BASE_ADDR + 4*3)
#define REG_ARG2        (BASE_ADDR + 4*4)
#define REG_ARG3        (BASE_ADDR + 4*5)
#define REG_RES0        (BASE_ADDR + 4*6)
#define REG_RES1        (BASE_ADDR + 4*7)
#define REG_RES2        (BASE_ADDR + 4*8)
#define REG_RES3        (BASE_ADDR + 4*9)#define BIT_RPC_START   (1 << 0)
#define BIT_RPC_READY   (1 << 1)
#define BIT_RPC_DONE    (1 << 2)
#define BIT_RPC_VALID   (1 << 3)void rpc_call(u32 method, u32 a0, u32 a1, u32 a2, u32 a3) {Xil_Out32(REG_METHOD, method);Xil_Out32(REG_ARG0, a0);Xil_Out32(REG_ARG1, a1);Xil_Out32(REG_ARG2, a2);Xil_Out32(REG_ARG3, a3);Xil_Out32(REG_CTRL, BIT_RPC_READY | BIT_RPC_START);int timeout_cnt = 0;const int TIMEOUT_MAX = 100;while (Xil_In32(REG_CTRL) & BIT_RPC_DONE == 0) {if (++timeout_cnt > TIMEOUT_MAX) {xil_printf("Timeout: RPC method %d failed.\n", method);Xil_Out32(REG_CTRL, 0x00);return;}}Xil_Out32(REG_CTRL, 0x00);u32 r0 = Xil_In32(REG_RES0);u32 r1 = Xil_In32(REG_RES1);u32 r2 = Xil_In32(REG_RES2);u32 r3 = Xil_In32(REG_RES3);xil_printf("RPC method %d result:\n", method);xil_printf("  res0 : %d=%d+%d\n", r0,a0,a1);xil_printf("  res1 : %d=%d+%d\n", r1,a2,a3);xil_printf("  res2 : %d=%d+%d\n", r2,a0,a2);xil_printf("  res3 : %d=%d+%d\n", r3,a1,a3);
}void dump_registers() {u32 val;xil_printf("AXI Register Dump (HEX + DEC):\n");val = Xil_In32(REG_CTRL);xil_printf("  mem[0]  REG_CTRL   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_METHOD);xil_printf("  mem[1]  REG_METHOD = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_ARG0);xil_printf("  mem[2]  REG_ARG0   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_ARG1);xil_printf("  mem[3]  REG_ARG1   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_ARG2);xil_printf("  mem[4]  REG_ARG2   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_ARG3);xil_printf("  mem[5]  REG_ARG3   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_RES0);xil_printf("  mem[6]  REG_RES0   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_RES1);xil_printf("  mem[7]  REG_RES1   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_RES2);xil_printf("  mem[8]  REG_RES2   = 0x%08X  (%10u)\n", val, val);val = Xil_In32(REG_RES3);xil_printf("  mem[9]  REG_RES3   = 0x%08X  (%10u)\n", val, val);
}int main() {xil_printf("AXI RPC Test Console. Type 't' and press Enter to test.\n");char cmd;int index;u32 value;static int p=0;while (1) {xil_printf("> ");if (scanf(" %c", &cmd) != 1)continue;if (cmd == 't' || cmd == 'T') {rpc_call(1, 1, 2, 3, 4);rpc_call(1, 10, 20, 30, 40);continue;}switch (cmd){case 'r':if (scanf("%d", &index) == 1){if (index < 0 || index > MAX_INDEX){xil_printf("Error: index out of range [0 ~ %d]\r\n", MAX_INDEX);while (getchar() != '\n');continue;}u32 read_val = Xil_In32(BASE_ADDR + 4 * index);xil_printf("[r %d] = 0x%08X / %u\r\n", index, read_val, read_val);}else{xil_printf("Invalid input. Use: r <index>\r\n");while (getchar() != '\n');}break;case 'w':if (scanf("%d %u", &index, &value) == 2){if (index < 0 || index > MAX_INDEX){xil_printf("Error: index out of range [0 ~ %d]\r\n", MAX_INDEX);while (getchar() != '\n');continue;}Xil_Out32(BASE_ADDR + 4 * index, value);xil_printf("[w %d] = 0x%08X / %u\r\n", index, value, value);}else{xil_printf("Invalid input. Use: w <index> <value>\r\n");while (getchar() != '\n');}break;case 'l':{dump_registers();break;}default:xil_printf("Unknown command '%c'. Use 'r' or 'w'.\r\n", cmd);while (getchar() != '\n');break;}}return 0;
}

测试结果

[16:27:01.812]发→◇t
□
[16:27:01.815]收←◆RPC method 1 result:res0 : 3=1+2res1 : 7=3+4res2 : 4=1+3res3 : 6=2+4
RPC method 1 result:res0 : 30=10+20res1 : 70=30+40res2 : 40=10+30res3 : 60=20+40
> 

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

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

相关文章

[spring-cloud: NamedContextFactory ClientFactoryObjectProvider]-源码阅读

依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-commons</artifactId><version>4.3.0</version> </dependency>源码 NamedContextFactory NamedContextFactory 类通过创建多个子…

HBase MOB技术特点及使用场景介绍

在 HBase 2.0 版本之前,虽然 HBase 能够存储从 1 字节到 10MB 大小的二进制对象 ,但其读写路径主要针对小于 100KB 的值进行了优化。当面对大量大小在 100KB - 10MB 之间的数据时,传统的存储方式就会暴露出问题。例如,当存储大量的图片、文档或短视频等中等大小对象时,由于…

Ubuntu 配置密钥+密码登录

目录 1、密钥生成 2、发送公钥至 需要连接的服务器 3、选用私钥登录 1、密钥生成 ssh-keygen -t rsa -b 4096 -C "angindem"2、发送公钥至 需要连接的服务器 将.ssh中的id_rsa.pub 的密钥&#xff0c;放在authorized_keys中 注意&#xff1a;.ssh 文件夹一定赋予…

谷歌浏览器Chrome 缓存迁移

步骤 1&#xff1a;准备数据迁移1. 关闭 Chrome 及所有后台进程在任务管理器&#xff08;CtrlShiftEsc&#xff09;中结束所有 chrome.exe 进程。 2. 备份并移动原数据- 将 C:\Users\xxx\AppData\Local\Google\Chrome\User Data **整个文件夹**复制到新位置&#xff08;如 G:\…

Java中的RabbitMQ完全指南

Java中的RabbitMQ完全指南 1. 引言 什么是RabbitMQ RabbitMQ是一个开源的消息代理和队列服务器&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。它充当应用程序之间的消息中间件&#xff0c;允许分布式系统中的不同组件进行异步通信。RabbitMQ使用Erlang语言…

【MCAL】AUTOSAR架构下SPI数据异步DMA收发具体实现

目录 前言 正文 1.依赖的硬件特性 1.1.SPI硬件特性 1.1.1. TXFIFO Single Move Mode 1.1.2. RXFIFO Single Move Mode 1.1.3. Move Counter模式 1.1.4. PT中断 1.2.IR硬件特性 1.3.DMA硬件特性 1.3.1. DMA通道硬件请求 1.3.2. DMA循环Buffer 1.3.3. DMA Link List …

【Unity】协程 Async

协程 协程是 Unity 内置的异步机制&#xff0c;通过 yield 暂停执行&#xff0c;实现任务在多帧中分段执行。与普通函数不同&#xff0c;协程可在执行过程中挂起和恢复&#xff0c;呈现"并发"效果&#xff0c;但本质上仍运行于主线程。若在协程中进行耗时操作&#…

《揭秘!10 分钟洞悉 Prompt、Function Calling、MCP 与 AI agent 奥秘》

Prompt、Function Calling、MCP、AI agent这些术语频繁闯入我们的视野&#xff0c;它们到底都是什么、有啥关系。只需十分钟&#xff0c;咱们抽丝剥茧&#xff0c;揭开它们的神秘面纱&#xff0c;轻松掌握这些关键概念 并了解AI agent 完整执行流程。 一、提示词&#xff08;P…

决策树(回归树)全解析:原理、实践与应用

文章目录一、概述1.1 介绍1.2 回归树和分类树区别二、重要参数、属性及接口2.1 criterion&#xff08;不纯度衡量指标&#xff09;2.2 回归树如何工作&#xff08;核心流程拆解&#xff09;三、用回归树拟合正弦曲线&#xff08;实战案例&#xff09;3.1 绘制正弦曲线3.2 为正弦…

【盘古100Pro+开发板实验例程】FPGA学习 | HDMI 回环实验

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 1. 实验简介 实验目的&#xff1a; 完成 HDMI 回环实验 实验环境&#xff1a; Window11 PDS2022.2-SP6.4 硬件环境…

鸿蒙系统PC安装指南

鸿蒙系统PC安装指南一、安装DevEco Studio集成开发环境二、下载鸿蒙系统PC三、启动鸿蒙系统及使用一、安装DevEco Studio集成开发环境首先访问华为官网上&#xff0c;注册并登录华为账号&#xff0c;以开始下载所需的软件。若尚未注册&#xff0c;请先注册一个。在官网页面中&a…

三十九、【扩展工具篇】Allpairspy 组合用例生成器:智能设计高效测试集

三十九、【扩展工具篇】Allpairspy 组合用例生成器:智能设计高效测试集 前言 准备工作 第一部分:后端实现 - `allpairspy` API 1. 创建 `allpairspy` 服务 2. 创建 `allpairspy` API 视图 3. 注册 API 路由 第二部分:前端实现 - `Allpairspy` 工具界面 1. 创建 API 服务 (`s…

ZooKeeper 深度实践:从原理到 Spring Boot 全栈落地

在 Kubernetes 为主流注册发现的今天&#xff0c;给出如何在 Spring Boot 中基于 ZooKeeper 实现服务注册/发现、分布式锁、配置中心以及集群协调的完整代码与最佳实践。所有示例均可直接复制运行。 1. ZooKeeper 架构与核心原理 1.1 角色 Leader&#xff1a;处理写请求&…

可验证随机函数-VRF

可验证随机函数&#xff08;Verifiable Random Function, VRF&#xff09;是一种结合密码学技术的伪随机数生成器&#xff0c;其核心特点是生成的随机数可被公开验证&#xff0c;且具有不可预测性和唯一性。以下是VRF的详细解析&#xff1a;1. 基本定义与核心特性 可验证性&…

极客大挑战2020(部分wp)

Roamphp1-Welcome 405请求方法不允许&#xff0c;改一下请求方法 数组绕过&#xff0c;在页面搜索flag即可&#xff01;本题&#xff1a;就是知道了405是请求方法不允许&#xff01; Roamphp2-Myblog&#xff08;zip协议加文件包含&#xff09; 首先进来就是一个博客页面&…

ESP32 外设驱动开发指南 (ESP-IDF框架)——GPIO篇:基础配置、外部中断与PWM(LEDC模块)应用

目录 一、前言 二、GPIO 2.1 GPIO简介 2.2 GPIO函数解析 2.3 LED驱动 2.4 KEY驱动 三、EXIT 3.1 EXIT简介 3.2 EXIT函数解析 3.3 EXIT驱动 四、LEDC 4.1 PWM原理解析 4.2 ESP32的LED PWM控制器介绍 4.3 LEDC函数解析 4.3.1 SW_PWM 4.3.2 HW_PWM 4.4 LEDC驱动 …

鸿蒙 ArkWeb 加载优化方案详解(2025 最佳实践)

适用平台&#xff1a;HarmonyOS NEXT / API 10 关键词&#xff1a;ArkWeb、WebviewController、NodeController、预加载、预连接、预渲染、性能优化一、前言&#xff1a;为什么必须优化 ArkWeb 加载&#xff1f;在鸿蒙生态中&#xff0c;ArkWeb 是系统级的 Web 容器引擎&#x…

JavaScript案例(乘法答题游戏)

项目概述 使用原生JavaScript实现一个乘法答题游戏&#xff0c;随机生成乘法题目&#xff0c;判断答案正误并记录分数&#xff0c;通过localStorage实现分数持久化存储。 核心功能需求 随机题目生成&#xff1a;动态生成1-10之间的乘法题答题交互&#xff1a;输入答案并提交…

EXCEL删除数据透视表

wps版 点击红框内任意区域 在顶部工具栏选择删除Excel 版 1.点击红框内任意区域2. 点击Enable Selection,再按住键盘上的Delete键&#xff0c;记住不是Backspace键

Python 飞机大战:从零开发经典 2D 射击游戏

引言&#xff1a;重温经典游戏开发 飞机大战作为经典的 2D 射击游戏&#xff0c;承载了许多人的童年回忆。使用 Python 和 Pygame 开发这样一款游戏不仅能重温经典&#xff0c;更是学习游戏开发绝佳的实践项目。本文将带你从零开始&#xff0c;一步步实现一个完整的飞机大战游…