- 代码实现对OUTPUT_NODES个32位浮点数进行RELU操作。
- 32位浮点数的二进制表示遵循 IEEE 754 标准,通常称为单精度浮点数。这个标准定义了浮点数的表示方法,具体分为三个部分:
- 符号位 (1 bit): 用于表示浮点数的正负。( 0 表示正数,1 表示负数)
- 指数位 (8 bits): 用于表示指数部分,并采用偏移量 (bias) 编码。
- 尾数 (或称为有效数字, 23 bits): 用于表示浮点数的有效数字部分。
Module
// https://github.com/omarelhedaby/CNN-FPGA/blob/master/Final%20Code%20Files/Part%205-%20Integration/activationFunction.v
module activationFunction(clk, reset, en, input_fc, output_fc);
// 模块定义,名称为 activationFunction
// 输入信号:
// - clk: 时钟信号
// - reset: 复位信号
// - en: 使能信号
// - input_fc: 输入数据,宽度为 DATA_WIDTH*OUTPUT_NODES
// 输出信号:
// - output_fc: 输出数据,宽度为 DATA_WIDTH*OUTPUT_NODES
// 参数定义
parameter DATA_WIDTH = 32; // 数据宽度,默认为 32 位
parameter OUTPUT_NODES = 32; // 输出节点数,默认为 32 个
// 输入信号定义
input clk, reset, en; // 时钟信号、复位信号和使能信号
input [DATA_WIDTH*OUTPUT_NODES-1:0] input_fc; // 输入数据,总宽度为 DATA_WIDTH*OUTPUT_NODES
output reg [DATA_WIDTH*OUTPUT_NODES-1:0] output_fc; // 输出数据,总宽度同样为 DATA_WIDTH*OUTPUT_NODES
// 内部变量声明
integer i; // 循环变量 i
// 在时钟负边沿或复位信号正边沿触发的过程块
always @ (negedge clk or posedge reset) begin
// 检查复位信号
if (reset == 1'b1) begin
// 如果复位信号为高电平(1),则将输出数据清零
output_fc = 0;
end else begin
// 如果不是复位状态,则检查使能信号
if (en == 1'b1) begin
// 如果使能信号为高电平(1),则处理输入数据
for (i = 0; i < OUTPUT_NODES; i = i + 1) begin
// 对每一个输出节点执行以下操作
// 检查输入数据中对应数据位的符号位(最高位)
if (input_fc[DATA_WIDTH*i + DATA_WIDTH - 1] == 1'b1) begin
// 如果符号位为 1,表示该数据为负数或需要置为 0
// 对应的输出数据设置为 0
output_fc[DATA_WIDTH*i +: DATA_WIDTH] = 0;
end else begin
// 否则,将输入数据直接赋值给输出数据
output_fc[DATA_WIDTH*i +: DATA_WIDTH] = input_fc[DATA_WIDTH*i +: DATA_WIDTH];
end
end
end
end
end
endmodule
TestBench
// Code your testbench here
// or browse Examples
`timescale 1ns / 1ps
module tb_activationFunction;
// 参数定义
parameter DATA_WIDTH = 32;
parameter OUTPUT_NODES = 32;
// 测试平台中的信号
reg clk;
reg reset;
reg en;
reg [DATA_WIDTH*OUTPUT_NODES-1:0] input_fc;
wire [DATA_WIDTH*OUTPUT_NODES-1:0] output_fc;
// 实例化被测试模块
activationFunction #(
.DATA_WIDTH(DATA_WIDTH),
.OUTPUT_NODES(OUTPUT_NODES)
) uut (
.clk(clk),
.reset(reset),
.en(en),
.input_fc(input_fc),
.output_fc(output_fc)
);
// 时钟生成
initial begin
clk = 0;
forever #5 clk = ~clk; // 每 5 时间单位切换一次时钟状态
end
// 测试过程
initial begin
// 初始化信号
reset = 1;
en = 0;
input_fc = 0;
// 复位过程
#10;
reset = 0;
// 测试 1: 输入数据处理
#10;
en = 1;
input_fc = {32'h00000001, 32'h00000002, 32'h00000003, 32'h00000004,
32'h00000005, 32'h00000006, 32'h00000007, 32'h00000008,
32'h00000009, 32'h0000000A, 32'h0000000B, 32'h0000000C,
32'h0000000D, 32'h0000000E, 32'h0000000F, 32'h00000010,
32'h00000011, 32'h00000012, 32'h00000013, 32'h00000014,
32'h00000015, 32'h00000016, 32'h00000017, 32'h00000018,
32'h00000019, 32'h0000001A, 32'h0000001B, 32'h0000001C,
32'h0000001D, 32'h0000001E, 32'h0000001F, 32'h00000020};
// 等待处理完成
#10;
// 测试 2: 改变输入数据,测试负数的处理
input_fc = {32'h80000001, 32'h80000002, 32'h80000003, 32'h80000004,
32'h80000005, 32'h80000006, 32'h80000007, 32'h80000008,
32'h80000009, 32'h8000000A, 32'h8000000B, 32'h8000000C,
32'h8000000D, 32'h8000000E, 32'h8000000F, 32'h80000010,
32'h80000011, 32'h80000012, 32'h80000013, 32'h80000014,
32'h80000015, 32'h80000016, 32'h80000017, 32'h80000018,
32'h80000019, 32'h8000001A, 32'h8000001B, 32'h8000001C,
32'h8000001D, 32'h8000001E, 32'h8000001F, 32'h80000020};
// 等待处理完成
#10;
// 禁用模块
en = 0;
// 等待一段时间以观察状态
#10;
// 启用模块并测试复位
reset = 1;
#10;
reset = 0;
en = 1;
// 等待一段时间
#10;
// 结束仿真
$finish;
end
// 观察输出信号变化
//initial begin
// $monitor("Time: %0t, Output: %h", $time, output_fc);
//end
// edaplayground仿真https://blog.csdn.net/ResumeProject/article/details/139300170
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule
仿真
edaplayground
-
第一个时钟周期:rest为1,输出为0
-
第二个时钟周期:rest为0,输入为空,输出为0
-
第三个时钟周期:rest为0,输入为20,输出为20
-
第四个时钟周期:rest为0,输入为负数,输出为0
-