姓名:徐铭伟 学号:21011210001 学院:通信工程学院
【嵌牛导读】使用Verilog实现硬件堆栈
【嵌牛鼻子】硬件堆栈的Verilog实现
【嵌牛提问】如何使用Verilog实现硬件堆栈
【嵌牛正文】
文章目录
一、栈的 C 语言实现
1.1 顺序栈的结构定义
1.2 顺序栈的初始化
1.3 顺序栈的写入
1.4 顺序栈的读出
二、栈的 Verilog 实现
2.1 Verilog 实现代码
2.2 仿真测试
一、栈的 C 语言实现
1.1 顺序栈的数据结构定义
data为栈中每个元素的数据值;top为栈顶的指针,在计算机体系中也称作sp 指针。
/* 数据结构定义 */
typedef struct
{
int data[MAXSIZE]; // 栈数据空间
int top; // 栈顶指针
}SqStack; // 顺序栈
1.2 顺序栈的初始化
在栈中,我们定义sp 指针初始指向-1,表示这是一个空栈,而当 sp 指向 0 时则代表栈中存有一个数据。
// 栈初始化
void Stack_Init(SqStack *S)
{
S->top = -1;
for (unsigned int i = 0; i < MAXSIZE; i++)
S->data[i] = 0;
}
1.4 顺序栈的读出
从栈中读出数据又称弹栈或出栈,在执行读数据操作之前需要判断栈是否已经读空,方法是当 sp 指针指回 -1 时认为栈已经读空。若栈未空,则先读出当前 sp 指针指向空间的数据,然后将 sp 指针自减,指向上一个存储单元。
// 弹栈操作
char Pop(SqStack *S, int *e)
{
if( S->top == -1 )
{
return -1; // 栈空
}
*e = S->data[S->top]; // 弹出的数据赋值给e
S->top--; // 栈顶自减
return 1;
}
二、栈的 Verilog 实现
堆栈是现代计算机设备不可或缺的组成部分之一,局部变量、跳转语句、函数跳转的现场保存等,都需要使用堆栈(栈)来实现。目前主流的计算机及嵌入式处理器大都采用内存实现堆栈的方式,即在主存中开辟一块空间来保存堆栈数据。然而在一些低端嵌入式微控制器,例如 PIC 单片机中则会使用硬件堆栈,因为它们的内存不够大而且对堆栈大小的需求也不高。
基于上述的软件堆栈原理,本节使用 Verilog HDL 实现硬件同步堆栈并给出仿真测试。
2.1 Verilog 实现代码
以下为栈缓存的 verilog 实现代码。
① sp 指针在空栈时为 -1 (补码形式),并且约束其在栈顶和栈底的自增条件。
② 栈的判满和判空使用两个一段状态机实现。
③ 参数WRITE_GUARD可配置此栈缓存为写满保护或无写满保护模式。写满保护模式即是当栈满后,再将新数据压栈时不会将栈顶的旧数据冲刷掉。WRITE_GUARD = "ON"时打开写满保护、WRITE_GUARD = "OFF"时关闭写满保护。
//**********************************************
//COPYRIGHT(c)2021, XXX University
//All rights reserved.
//
//File name : Sync_Stack.v
//Module name : Sync_Stack
//Full name :
//Author : XXX
//Email :
//
//Version :
//This File is Created on 2021-09-24 13:27:27
//------------------Discription-----------------
//
//----------------------------------------------
//-------------Modification history-------------
//Last Modified by : XXX
//Last Modified time: 2021-09-24 13:27:27
//Discription :
//----------------------------------------------
//TIMESCALE
`timescale 1ns/1ns
//DEFINES
//`include ".v"
//----------------------------------------------
/*** 硬件堆栈 LIFO ***/
module Sync_Stack #(
parameter WRITE_GUARD = "ON" , // 模式配置:“ON”开启栈顶写保护; “OFF”关闭写保护
parameter DATA_WIDTH = 32 , // 数据位宽
parameter STACK_WIDTH = 8 // log2(堆栈大小)
)
(
input sclk , // 同步时钟
input rst_n , // 低复位
input wr_en , // 写使能
input [DATA_WIDTH-1:0] din , // 写数据
output reg full , // 写满标志
input rd_en , // 读使能
output reg [DATA_WIDTH-1:0] dout , // 读数据
output reg empty // 读空标志
);
localparam STACK_DEPTH = 2**STACK_WIDTH;
reg [DATA_WIDTH-1:0] syc_ram [STACK_DEPTH-1:0]; // RAM
reg [STACK_WIDTH:0] sp; // SP指针
wire [STACK_WIDTH:0] index = $signed(sp) + 1; // ram元素索引
// RAM读写控制
generate
// 堆栈写满保护模式
if( WRITE_GUARD == "ON" ) begin
reg guard_flag; // 栈顶写满保护标志
always @(posedge sclk or negedge rst_n) begin : wr_rd
integer i;
if ( rst_n == 1'b0 ) begin
for( i=0; i syc_ram[i] <= 'd0; dout <= 'd0; guard_flag <= 1'b0; end else begin if( wr_en & rd_en ) begin dout <= din; guard_flag <= 1'b0; end else if( wr_en && (index < STACK_DEPTH) ) begin if( index < (STACK_DEPTH - 1) ) begin syc_ram[index] <= din; end else begin if( guard_flag == 1'b0 ) begin syc_ram[index] <= din; guard_flag <= 1'b1; end end end else if( rd_en ) begin dout <= syc_ram[index]; guard_flag <= 1'b0; end end end end // 无写满保护模式 else begin always @(posedge sclk or negedge rst_n) begin : wr_rd integer i; if ( rst_n == 1'b0 ) begin for( i=0; i syc_ram[i] <= 'd0; dout <= 'd0; end else begin if( wr_en & rd_en ) begin dout <= din; end else if( wr_en && (index < STACK_DEPTH) ) begin syc_ram[index] <= din; end else if( rd_en ) begin dout <= syc_ram[index]; end end end end endgenerate // SP指针控制 always @(posedge sclk or negedge rst_n) begin if ( rst_n == 1'b0 ) begin sp <= -1; end else begin if( wr_en & rd_en ) begin sp <= sp; end else if( wr_en && (index < STACK_DEPTH - 1) ) begin sp <= sp + 1'b1; end else if( rd_en && (index > 'd0) ) begin sp <= sp - 1'b1; end else begin sp <= sp; end end end // 栈满判断状态机 reg full_state; always @(posedge sclk or negedge rst_n) begin if ( rst_n == 1'b0 ) begin full_state <= 1'b0; full <= 1'b0; end else begin case( full_state ) // 未满状态 1'b0 : begin if( wr_en && (index == STACK_DEPTH - 1) ) begin full <= 1'b1; full_state <= 1'b1; end else begin full <= 1'b0; full_state <= 1'b0; end end // 栈满状态 1'b1 : begin if( rd_en ) begin full <= 1'b0; full_state <= 1'b0; end else begin full <= 1'b1; full_state <= 1'b1; end end endcase end end // 栈空判断状态机 reg empty_state; always @(posedge sclk or negedge rst_n) begin if ( rst_n == 1'b0 ) begin empty_state <= 1'b1; empty <= 1'b1; end else begin case( empty_state ) // 未空状态 1'b0 : begin if( rd_en && (index == 'd0) ) begin empty <= 1'b1; empty_state <= 1'b1; end else begin empty <= 1'b0; empty_state <= 1'b0; end end // 栈空状态 1'b1 : begin if( wr_en ) begin empty <= 1'b0; empty_state <= 1'b0; end else begin empty <= 1'b1; empty_state <= 1'b1; end end endcase end end endmodule 2.2 仿真测试 Testbench 代码如下所示。 //********************************************** //COPYRIGHT(c)2021, XXX University //All rights reserved. // //File name : testbench.v //Module name : testbench //Full name : //Author : XXX //Email : // //Version : //This File is Created on 2021-09-24 13:27:08 //------------------Discription----------------- // //---------------------------------------------- //-------------Modification history------------- //Last Modified by : XXX //Last Modified time: 2021-09-24 13:27:08 //Discription : //---------------------------------------------- //TIMESCALE `timescale 1ns/1ns //DEFINES //`include ".v" //---------------------------------------------- `define DATA_WIDTH 32 `define STACK_WIDTH 4 module testbench; reg sclk ; reg rst_n ; reg wr_en ; reg [`DATA_WIDTH-1:0] din ; wire full ; reg rd_en ; wire [`DATA_WIDTH-1:0] dout ; wire empty ; // 生成时钟 initial begin sclk = 0; forever #5 sclk = ~sclk; end // Main initial begin rst_n = 0; wr_en = 0; din = 0; rd_en = 0; #50 rst_n = 1; #50 repeat( 2**`STACK_WIDTH + 1 ) begin @(posedge sclk) begin wr_en <= 1'b1; din <= din + 1; end end @(posedge sclk) wr_en <= 1'b0; repeat( 2**`STACK_WIDTH + 1 ) begin @(posedge sclk) begin rd_en <= 1'b1; end end @(posedge sclk) rd_en <= 1'b0; #50 repeat( 2**`STACK_WIDTH/2 ) begin @(posedge sclk) begin wr_en <= 1'b1; din <= din + 1; end end @(posedge sclk) wr_en <= 1'b0; #50 repeat( 2**`STACK_WIDTH/2 ) begin @(posedge sclk) begin wr_en <= 1'b1; din <= din + 1; end end @(posedge sclk) wr_en <= 1'b0; repeat( 2**`STACK_WIDTH/2 ) begin @(posedge sclk) begin rd_en <= 1'b1; end end @(posedge sclk) rd_en <= 1'b0; #50 repeat( 2**`STACK_WIDTH/2 ) begin @(posedge sclk) begin rd_en <= 1'b1; end end @(posedge sclk) rd_en <= 1'b0; #100 $stop; end // 同步堆栈例化 Sync_Stack #( .WRITE_GUARD("ON"), .DATA_WIDTH (`DATA_WIDTH), .STACK_WIDTH(`STACK_WIDTH) ) U_Sync_Stack ( .sclk (sclk), .rst_n (rst_n), .wr_en (wr_en), .din (din), .full (full), .rd_en (rd_en), .dout (dout), .empty (empty) ); endmodule 开启写满保护模式的栈缓存读写波形如下。测试中栈深度为 16 ,对其写 17 次,可见第 17 个数据“32’d17”并没有写入栈中。随后对其读 17 次,可以看到之前写入的数据都按 LIFO 顺序被读了出来。 关闭写满保护模式的波形如下。测试激励同上,可以看到读取时原来的数据 “32’d16” 的位置被 “32’d17” 覆盖了。