Skip to content

Instantly share code, notes, and snippets.

@ZipCPU
Created October 16, 2020 22:37
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZipCPU/497c85a0dc7b84e9cd05b3ddf1bc2487 to your computer and use it in GitHub Desktop.
Save ZipCPU/497c85a0dc7b84e9cd05b3ddf1bc2487 to your computer and use it in GitHub Desktop.
Verified AXI-lite slave design
## Here's the SymbiYosys script we used for this task
##
[tasks]
prf
cvr
[options]
prf: mode prove
prf: depth 4
cvr: mode cover
cvr: depth 40
[engines]
smtbmc
[script]
read -formal axi_regs_top.sv
read -formal faxil_slave.v
prep -top axi_regs_top
[files]
axi_regs_top.sv
faxil_slave.v
//
// This was drawn from the original design found at:
//
// https://github.com/olofk/axi_node/blob/master/axi_regs_top.sv
//
// Changes made:
// 1) I added VIM folding: // {{{ and // }}}
// 2) I added a formal property section between the `ifdef FORMAL and
// `endif at the bottom of the file, and
// 3) I fixed the AXI bug in the data_out_reg definition.
//
// You can find the formal AXI-lite properties at:
//
// https://github.com/ZipCPU/wb2axip/blob/master/bench/formal/faxil_slave.v
//
// I used the commercial version of SymbiYosys to formally verify this core.
// Verilog AXI-lite designs are usually really easy to verify with the open-source
// version of SymbiYosys, however, this design had 3D arrays in output ports
// which the open source version of SymbiYosys doesn't yet support.
//
// Copyright 2015 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the “License”); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// ============================================================================= //
// Company: Multitherman Laboratory @ DEIS - University of Bologna //
// Viale Risorgimento 2 40136 //
// Bologna - fax 0512093785 - //
// //
// Engineer: Antonio Pullini - pullinia@iis.ee.ethz.ch //
// Igor Loi - igor.loi@unibo.it //
// //
// //
// Additional contributions by: //
// //
// //
// //
// Create Date: 01/02/2014 //
// Design Name: AXI 4 INTERCONNECT //
// Module Name: axi_regs_top //
// Project Name: PULP //
// Language: SystemVerilog //
// //
// Description: This block allows for the node reconfiguration. basically is //
// possible to remap each master port (memory mapped) and to //
// define the connectivity map between masters and slaves. //
// It is possible to define up to 4 regions for each master port.//
// Each Region is made of Start address, end address and Valid //
// rule. Connettivity map is stored in a 32 bit register, //
// therefore it is not possible to have more than 32 slave ports //
// The memory map is the FOLLOWING: //
// //
// @0x00 --> START ADDR REGION 0 , MASTER PORT 0 //
// @0x04 --> END ADDR REGION 0 , MASTER PORT 0 //
// @0x08 --> VALID RULE REGION 0 , MASTER PORT 0 //
// @0x0C --> NOT USED //
// @0x10 --> START ADDR REGION 1 , MASTER PORT 0 //
// @0x14 --> END ADDR REGION 1 , MASTER PORT 0 //
// @0x18 --> VALID RULE REGION 1 , MASTER PORT 0 //
// @0x1C --> NOT USED //
// @0x20 --> START ADDR REGION 2 , MASTER PORT 0 //
// @0x24 --> END ADDR REGION 2 , MASTER PORT 0 //
// @0x28 --> VALID RULE REGION 2 , MASTER PORT 0 //
// @0x2C --> NOT USED //
// @0x30 --> START ADDR REGION 3 , MASTER PORT 0 //
// @0x34 --> END ADDR REGION 3 , MASTER PORT 0 //
// @0x38 --> VALID RULE REGION 3 , MASTER PORT 0 //
// @0x3C --> NOT USED //
// //
// @0x40 --> START ADDR REGION 0 , MASTER PORT 1 //
// @0x44 --> END ADDR REGION 0 , MASTER PORT 1 //
// @0x48 --> VALID RULE REGION 0 , MASTER PORT 1 //
// @0x4C --> NOT USED //
// @0x50 --> START ADDR REGION 1 , MASTER PORT 1 //
// @0x54 --> END ADDR REGION 1 , MASTER PORT 1 //
// @0x58 --> VALID RULE REGION 1 , MASTER PORT 1 //
// @0x5C --> NOT USED //
// @0x60 --> START ADDR REGION 2 , MASTER PORT 1 //
// @0x64 --> END ADDR REGION 2 , MASTER PORT 1 //
// @0x68 --> VALID RULE REGION 2 , MASTER PORT 1 //
// @0x6C --> NOT USED //
// @0x70 --> START ADDR REGION 3 , MASTER PORT 1 //
// @0x74 --> END ADDR REGION 3 , MASTER PORT 1 //
// @0x78 --> VALID RULE REGION 3 , MASTER PORT 1 //
// @0x7C --> NOT USED //
// ..... //
// ..... //
// @0x1000 --> CONNECTIVITY MAP MASTER 0 //
// @0x1004 --> CONNECTIVITY MAP MASTER 1 //
// @0x1008 --> CONNECTIVITY MAP MASTER 2 //
// @0x100C --> CONNECTIVITY MAP MASTER 3 //
// ..... //
// ..... //
// Revision: //
// Revision v0.1 - 01/02/2014 : File Created //
// Revision v0.2 - 30/05/2014 : Design adapted for the axi node //
// Revision v0.3 - 18/11/2014 : Modification in the memory map //
// //
// //
// //
// //
// //
// ============================================================================= //
module axi_regs_top
#(
parameter C_S_AXI_ADDR_WIDTH = 32,
parameter C_S_AXI_DATA_WIDTH = 32,
parameter N_REGION_MAX = 4,
parameter N_MASTER_PORT = 16,
parameter N_SLAVE_PORT = 16
)
(
input logic s_axi_aclk,
input logic s_axi_aresetn,
// AXI slave ports
// {{{
// ADDRESS WRITE CHANNEL
input logic [C_S_AXI_ADDR_WIDTH-1:0] s_axi_awaddr,
input logic s_axi_awvalid,
output logic s_axi_awready,
// ADDRESS READ CHANNEL
input logic [C_S_AXI_ADDR_WIDTH-1:0] s_axi_araddr,
input logic s_axi_arvalid,
output logic s_axi_arready,
// ADDRESS WRITE CHANNEL
input logic [C_S_AXI_DATA_WIDTH-1:0] s_axi_wdata,
input logic [C_S_AXI_DATA_WIDTH/8-1:0] s_axi_wstrb,
input logic s_axi_wvalid,
output logic s_axi_wready,
input logic s_axi_bready,
output logic [1:0] s_axi_bresp,
output logic s_axi_bvalid,
// RESPONSE READ CHANNEL
output logic [C_S_AXI_DATA_WIDTH-1:0] s_axi_rdata,
output logic s_axi_rvalid,
input logic s_axi_rready,
output logic [1:0] s_axi_rresp,
// }}}
input logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0][C_S_AXI_DATA_WIDTH-1:0] init_START_ADDR_i,
input logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0][C_S_AXI_DATA_WIDTH-1:0] init_END_ADDR_i,
input logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0] init_valid_rule_i,
input logic [N_SLAVE_PORT-1:0][N_MASTER_PORT-1:0] init_connectivity_map_i,
output logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0][C_S_AXI_DATA_WIDTH-1:0] START_ADDR_o,
output logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0][C_S_AXI_DATA_WIDTH-1:0] END_ADDR_o,
output logic [N_REGION_MAX-1:0][N_MASTER_PORT-1:0] valid_rule_o,
output logic [N_SLAVE_PORT-1:0][N_MASTER_PORT-1:0] connectivity_map_o
);
// Local register declarations
// {{{
localparam NUM_REGS = N_MASTER_PORT*4*N_REGION_MAX + N_SLAVE_PORT;
logic [N_SLAVE_PORT-1:0] [C_S_AXI_DATA_WIDTH-1:0] temp_reg;
reg awaddr_done_reg;
reg awaddr_done_reg_dly;
reg wdata_done_reg;
reg wdata_done_reg_dly;
reg wresp_done_reg;
reg wresp_running_reg;
reg araddr_done_reg;
reg rresp_done_reg;
reg rresp_running_reg;
reg awready;
reg wready;
reg bvalid;
reg arready;
reg rvalid;
reg [C_S_AXI_ADDR_WIDTH-1:0] waddr_reg;
reg [C_S_AXI_DATA_WIDTH-1:0] wdata_reg;
reg [3:0] wstrb_reg;
reg [C_S_AXI_ADDR_WIDTH-1:0] raddr_reg;
reg [C_S_AXI_DATA_WIDTH-1:0] data_out_reg;
logic write_en;
integer byte_index;
integer k,y,w;
genvar i,j;
wire wdata_done_rise;
wire awaddr_done_rise;
logic [NUM_REGS-1:0][C_S_AXI_DATA_WIDTH/8-1:0][7:0] cfg_reg; //32bit registers
logic [N_SLAVE_PORT-1:0][C_S_AXI_DATA_WIDTH-1:0] init_connectivity_map_s; //32bit registers
// }}}
assign write_en = (wdata_done_rise & awaddr_done_reg) | (awaddr_done_rise & wdata_done_reg);
assign wdata_done_rise = wdata_done_reg & ~wdata_done_reg_dly;
assign awaddr_done_rise = awaddr_done_reg & ~awaddr_done_reg_dly;
// aw & w done_reg_dly registers
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
wdata_done_reg_dly <= 0;
awaddr_done_reg_dly <= 0;
end
else
begin
wdata_done_reg_dly <= wdata_done_reg;
awaddr_done_reg_dly <= awaddr_done_reg;
end
end
// }}}
// WRITE ADDRESS CHANNEL logic
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
awaddr_done_reg <= 0;
waddr_reg <= 0;
awready <= 1;
end
else
begin
if (awready && s_axi_awvalid)
begin
awready <= 0;
awaddr_done_reg <= 1;
waddr_reg <= {2'b00,s_axi_awaddr[31:2]};
end
else if (awaddr_done_reg && wresp_done_reg)
begin
awready <= 1;
awaddr_done_reg <= 0;
end
end
end
// }}}
// WRITE DATA CHANNEL logic
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
wdata_done_reg <= 0;
wready <= 1;
wdata_reg <= 0;
wstrb_reg <= 0;
end
else
begin
if (wready && s_axi_wvalid)
begin
wready <= 0;
wdata_done_reg <= 1;
wdata_reg <= s_axi_wdata;
wstrb_reg <= s_axi_wstrb;
end
else if (wdata_done_reg && wresp_done_reg)
begin
wready <= 1;
wdata_done_reg <= 0;
end
end
end
// }}}
// WRITE RESPONSE CHANNEL logic
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
bvalid <= 0;
wresp_done_reg <= 0;
wresp_running_reg <= 0;
end
else
begin
if (awaddr_done_reg && wdata_done_reg && !wresp_done_reg)
begin
if (!wresp_running_reg)
begin
bvalid <= 1;
wresp_running_reg <= 1;
end
else if (s_axi_bready)
begin
bvalid <= 0;
wresp_done_reg <= 1;
wresp_running_reg <= 0;
end
end
else
begin
bvalid <= 0;
wresp_done_reg <= 0;
wresp_running_reg <= 0;
end
end
end
// }}}
// READ ADDRESS CHANNEL logic
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
araddr_done_reg <= 0;
arready <= 1;
raddr_reg <= 0;
end
else
begin
if (arready && s_axi_arvalid)
begin
arready <= 0;
araddr_done_reg <= 1;
raddr_reg <= s_axi_araddr;
end
else if (araddr_done_reg && rresp_done_reg)
begin
arready <= 1;
araddr_done_reg <= 0;
end
end
end
// }}}
// READ RESPONSE CHANNEL logic
// {{{
always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn)
begin
if (!s_axi_aresetn)
begin
rresp_done_reg <= 0;
rvalid <= 0;
rresp_running_reg <= 0;
end
else
begin
if (araddr_done_reg && !rresp_done_reg)
begin
if (!rresp_running_reg)
begin
rvalid <= 1;
rresp_running_reg <= 1;
end
else if (s_axi_rready)
begin
rvalid <= 0;
rresp_done_reg <= 1;
rresp_running_reg <= 0;
end
end
else
begin
rvalid <= 0;
rresp_done_reg <= 0;
rresp_running_reg <= 0;
end
end
end
// }}}
generate
if(N_MASTER_PORT < 32)
begin
always @(*)
begin
for(w = 0; w < N_SLAVE_PORT; w++)
begin
init_connectivity_map_s[w][N_MASTER_PORT-1:0] = init_connectivity_map_i[w]; //there are w arrays, each is N_MASTER_PORT bit wide
init_connectivity_map_s[w][C_S_AXI_DATA_WIDTH-1:N_MASTER_PORT] = '0;
end
end
end
else // N_MASTER_PORT == 32
begin
always @(*)
begin
for(w = 0; w < N_SLAVE_PORT; w++)
begin
init_connectivity_map_s[w] = init_connectivity_map_i[w]; //there are w arrays, each is N_MASTER_PORT bit wide
end
end
end
endgenerate
always_ff @( posedge s_axi_aclk, negedge s_axi_aresetn )
begin
if ( s_axi_aresetn == 1'b0 )
begin
for(y = 0; y < N_REGION_MAX; y++)
begin
for(k = 0; k < N_MASTER_PORT; k++)
begin
cfg_reg[ (y*4) + (k*4*N_REGION_MAX) + 0] <= init_START_ADDR_i [y][k];
cfg_reg[ (y*4) + (k*4*N_REGION_MAX) + 1] <= init_END_ADDR_i [y][k];
cfg_reg[ (y*4) + (k*4*N_REGION_MAX) + 2][0] <= init_valid_rule_i [y][k];
cfg_reg[ (y*4) + (k*4*N_REGION_MAX) + 2][C_S_AXI_DATA_WIDTH-1:1] <= '0;
cfg_reg[ (y*4) + (k*4*N_REGION_MAX) + 3] <= 32'hDEADBEEF;
end
end
for(y = 0; y < N_SLAVE_PORT; y++)
begin
cfg_reg[N_MASTER_PORT*4*N_REGION_MAX + y ] <= init_connectivity_map_s[y];
end
end
else if (write_en)
begin
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( wstrb_reg[byte_index] == 1 )
cfg_reg[waddr_reg[7:0]][byte_index] <= wdata_reg[(byte_index*8) +: 8]; //TODO Handle Errors if we address unmapped address locations
end
end // SLAVE_REG_WRITE_PROC
generate
for(i=0;i<N_REGION_MAX;i++)
begin
for(j=0;j<N_MASTER_PORT;j++)
begin
assign START_ADDR_o [i][j] = cfg_reg[i*4 + j*4*N_REGION_MAX + 0];
assign END_ADDR_o [i][j] = cfg_reg[i*4 + j*4*N_REGION_MAX + 1];
assign valid_rule_o [i][j] = cfg_reg[i*4 + j*4*N_REGION_MAX + 2][0][0];
end
end
for(i = 0; i < N_SLAVE_PORT; i++)
begin
assign temp_reg[i] = cfg_reg[N_MASTER_PORT*4*N_REGION_MAX + i];
assign connectivity_map_o[i] = temp_reg[i][N_MASTER_PORT-1:0];
end
endgenerate
// implement slave model register read mux
always @(posedge s_axi_aclk)
if (araddr_done_reg && !rresp_running_reg)
data_out_reg <= cfg_reg[raddr_reg[7:0]];
assign s_axi_awready = awready;
assign s_axi_wready = wready;
assign s_axi_bresp = 2'b00;
assign s_axi_bvalid = bvalid;
assign s_axi_arready = arready;
assign s_axi_rresp = 2'b00;
assign s_axi_rvalid = rvalid;
assign s_axi_rdata = data_out_reg;
`ifdef FORMAL
localparam F_LGDEPTH = 4;
wire [F_LGDEPTH-1:0] faxil_rd_outstanding;
wire [F_LGDEPTH-1:0] faxil_wr_outstanding;
wire [F_LGDEPTH-1:0] faxil_awr_outstanding;
faxil_slave #(
// {{{
.C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH),
.C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
.F_OPT_ASYNC_RESET(1'b1),
.F_OPT_COVER_BURST(4),
.F_LGDEPTH(F_LGDEPTH)
// }}}
) faxil (
// {{{
.i_clk(s_axi_aclk),
.i_axi_reset_n(s_axi_aresetn),
//
.i_axi_awready(s_axi_awready),
.i_axi_awaddr(s_axi_awaddr),
.i_axi_awvalid(s_axi_awvalid),
.i_axi_awprot(3'h0),
.i_axi_awcache(4'h0),
//
.i_axi_wready(s_axi_wready),
.i_axi_wdata(s_axi_wdata),
.i_axi_wstrb(s_axi_wstrb),
.i_axi_wvalid(s_axi_wvalid),
//
.i_axi_bready(s_axi_bready),
.i_axi_bvalid(s_axi_bvalid),
.i_axi_bresp(s_axi_bresp),
//
.i_axi_araddr(s_axi_araddr),
.i_axi_arvalid(s_axi_arvalid),
.i_axi_arready(s_axi_arready),
.i_axi_arprot(3'h0),
.i_axi_arcache(4'h0),
//
.i_axi_rvalid(s_axi_rvalid),
.i_axi_rready(s_axi_rready),
.i_axi_rdata(s_axi_rdata),
.i_axi_rresp(s_axi_rresp),
//
.f_axi_rd_outstanding(faxil_rd_outstanding),
.f_axi_wr_outstanding(faxil_wr_outstanding),
.f_axi_awr_outstanding(faxil_awr_outstanding)
// }}}
);
////////////////////////////////////////////////////////////////////////
//
// Write checking
// {{{
////////////////////////////////////////////////////////////////////////
//
//
always @(*)
assert(faxil_awr_outstanding <= 1);
always @(*)
assert(faxil_wr_outstanding <= 1);
always @(*)
if (s_axi_aresetn)
assert(awaddr_done_reg == !s_axi_awready);
always @(*)
if (s_axi_aresetn)
assert(wdata_done_reg == !s_axi_wready);
always @(*)
if (s_axi_aresetn)
assert((awaddr_done_reg && !wresp_done_reg) == (faxil_awr_outstanding == 1));
always @(*)
if (s_axi_aresetn)
assert((wdata_done_reg && !wresp_done_reg) == (faxil_wr_outstanding == 1));
always @(*)
if (wresp_done_reg)
assert(wdata_done_reg && awaddr_done_reg);
always @(*)
if (s_axi_aresetn)
assert(wresp_running_reg == s_axi_bvalid);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Read checking
// {{{
////////////////////////////////////////////////////////////////////////
//
//
always @(*)
assert(faxil_rd_outstanding <= 1);
always @(*)
if (s_axi_aresetn)
assert((araddr_done_reg && !rresp_done_reg) == (faxil_rd_outstanding == 1));
always @(*)
if (s_axi_aresetn)
assert(araddr_done_reg || !rresp_done_reg);
always @(*)
if (s_axi_aresetn)
assert(araddr_done_reg == !s_axi_arready);
always @(*)
if (s_axi_aresetn)
assert(rresp_running_reg == s_axi_rvalid);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Bugs found
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// BUG #1: AXI_RDATA might change when stalled
// always @(posedge s_axi_aclk)
// if (s_axi_aresetn && $past(s_axi_rvalid && !s_axi_rready))
// assume($stable(s_axi_rdata));
// }}}
////////////////////////////////////////////////////////////////////////
//
// (Careless assumptions and/or cover)
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// }}}
`endif
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment