summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
committerAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
commit3038edc09a2eb15762f2e58533f429489107520b (patch)
treef7a45e424d39e6fef0d59e329c1bf6ea206e2886
parent3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff)
rtl/wb2axip: add to version control
Diffstat (limited to '')
-rw-r--r--rtl/mod.mk2
-rw-r--r--rtl/wb2axip/.gitignore2
-rw-r--r--rtl/wb2axip/Makefile344
-rw-r--r--rtl/wb2axip/README.md175
-rw-r--r--rtl/wb2axip/addrdecode.v461
-rw-r--r--rtl/wb2axip/afifo.v801
-rw-r--r--rtl/wb2axip/apbslave.v229
-rw-r--r--rtl/wb2axip/apbxclk.v610
-rw-r--r--rtl/wb2axip/axi2axi3.v897
-rw-r--r--rtl/wb2axip/axi2axilite.v1173
-rw-r--r--rtl/wb2axip/axi2axilsub.v2157
-rw-r--r--rtl/wb2axip/axi32axi.v376
-rw-r--r--rtl/wb2axip/axi3reorder.v743
-rw-r--r--rtl/wb2axip/axi_addr.v235
-rw-r--r--rtl/wb2axip/axidma.v2977
-rw-r--r--rtl/wb2axip/axidouble.v1406
-rw-r--r--rtl/wb2axip/axiempty.v490
-rw-r--r--rtl/wb2axip/axil2apb.v717
-rw-r--r--rtl/wb2axip/axil2axis.v883
-rw-r--r--rtl/wb2axip/axildouble.v734
-rw-r--r--rtl/wb2axip/axilempty.v371
-rw-r--r--rtl/wb2axip/axilfetch.v469
-rw-r--r--rtl/wb2axip/axilgpio.v808
-rw-r--r--rtl/wb2axip/axilite2axi.v374
-rw-r--r--rtl/wb2axip/axilrd2wbsp.v601
-rw-r--r--rtl/wb2axip/axilsafety.v1449
-rw-r--r--rtl/wb2axip/axilsingle.v714
-rw-r--r--rtl/wb2axip/axilupsz.v603
-rw-r--r--rtl/wb2axip/axilwr2wbsp.v675
-rw-r--r--rtl/wb2axip/axilxbar.v2421
-rw-r--r--rtl/wb2axip/axim2wbsp.v317
-rw-r--r--rtl/wb2axip/aximm2s.v2158
-rw-r--r--rtl/wb2axip/aximrd2wbsp.v740
-rw-r--r--rtl/wb2axip/aximwr2wbsp.v751
-rw-r--r--rtl/wb2axip/axiperf.v1603
-rw-r--r--rtl/wb2axip/axis2mm.v2234
-rw-r--r--rtl/wb2axip/axisafety.v2339
-rw-r--r--rtl/wb2axip/axisbroadcast.v192
-rw-r--r--rtl/wb2axip/axisgdma.v1120
-rw-r--r--rtl/wb2axip/axisgfsm.v1221
-rw-r--r--rtl/wb2axip/axispacker.v890
-rw-r--r--rtl/wb2axip/axisrandom.v136
-rw-r--r--rtl/wb2axip/axissafety.v677
-rw-r--r--rtl/wb2axip/axisswitch.v631
-rw-r--r--rtl/wb2axip/axivcamera.v1219
-rw-r--r--rtl/wb2axip/axivdisplay.v1438
-rw-r--r--rtl/wb2axip/axivfifo.v1405
-rw-r--r--rtl/wb2axip/axixbar.v2585
-rw-r--r--rtl/wb2axip/axixclk.v312
-rw-r--r--rtl/wb2axip/axlite2wbsp.v568
-rw-r--r--rtl/wb2axip/axlite_wrapper.vhd136
-rw-r--r--rtl/wb2axip/demoaxi.v720
-rw-r--r--rtl/wb2axip/demofull.v1285
-rw-r--r--rtl/wb2axip/easyaxil.v524
-rw-r--r--rtl/wb2axip/migsdram.v313
-rw-r--r--rtl/wb2axip/mod.mk10
-rw-r--r--rtl/wb2axip/sfifo.v482
-rw-r--r--rtl/wb2axip/sfifothresh.v100
-rw-r--r--rtl/wb2axip/skidbuffer.v495
-rw-r--r--rtl/wb2axip/wbarbiter.v404
-rw-r--r--rtl/wb2axip/wbc2pipeline.v188
-rw-r--r--rtl/wb2axip/wbm2axilite.v685
-rw-r--r--rtl/wb2axip/wbm2axisp.v1155
-rw-r--r--rtl/wb2axip/wbp2classic.v205
-rw-r--r--rtl/wb2axip/wbsafety.v587
-rw-r--r--rtl/wb2axip/wbxbar.v1790
-rw-r--r--rtl/wb2axip/wbxclk.v854
67 files changed, 56365 insertions, 1 deletions
diff --git a/rtl/mod.mk b/rtl/mod.mk
index b4dfee3..d431b8c 100644
--- a/rtl/mod.mk
+++ b/rtl/mod.mk
@@ -1,5 +1,5 @@
cores := config debounce intc
-subdirs := cache core dma_axi32 gfx perf picorv32 smp top
+subdirs := cache core dma_axi32 gfx perf picorv32 smp top wb2axip
define core/config
$(this)/rtl_include_dirs := .
diff --git a/rtl/wb2axip/.gitignore b/rtl/wb2axip/.gitignore
new file mode 100644
index 0000000..e1cd503
--- /dev/null
+++ b/rtl/wb2axip/.gitignore
@@ -0,0 +1,2 @@
+*.ys
+ivcheck
diff --git a/rtl/wb2axip/Makefile b/rtl/wb2axip/Makefile
new file mode 100644
index 0000000..db66615
--- /dev/null
+++ b/rtl/wb2axip/Makefile
@@ -0,0 +1,344 @@
+################################################################################
+##
+## Filename: Makefile
+## {{{
+## Project: WB2AXIPSP: bus bridges and other odds and ends
+##
+## Purpose: To describe how to build the Verilator libraries from the
+## RTL, for the purposes of trying to discover if they work.
+## Any actual testing will be done from the code within the bench/cpp
+## directory.
+##
+## Targets: The default target, all, builds the target test, which includes
+## the libraries necessary for Verilator testing.
+##
+## Creator: Dan Gisselquist, Ph.D.
+## Gisselquist Technology, LLC
+##
+################################################################################
+## }}}
+## Copyright (C) 2016-2023, Gisselquist Technology, LLC
+## {{{
+## This file is part of the WB2AXIP project.
+##
+## The WB2AXIP project contains free software and gateware, licensed under the
+## Apache License, Version 2.0 (the "License"). You may not use this project,
+## or this file, except in compliance with the License. You may obtain a copy
+## of the License at
+## }}}
+## http://www.apache.org/licenses/LICENSE-2.0
+## {{{
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the 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.
+##
+################################################################################
+##
+## }}}
+all: test
+YYMMDD=`date +%Y%m%d`
+CXX := g++
+IVCHECK := ivcheck
+IVERILOG := iverilog
+FBDIR := .
+VDIRFB:= $(FBDIR)/obj_dir
+VERILATOR := verilator
+VFLAGS := -MMD -O3 -Wall -Wpedantic -cc
+.DELETE_ON_ERROR:
+.PHONY: test
+test: testwb testaxi testaxil testapb testaxis
+
+.PHONY: testwb testaxi testaxil testapb testaxis
+
+.PHONY: axim2wbsp axim2wbsp wbm2axilite axilrd2wbsp axilwr2wbsp axlite2wbsp
+.PHONY: axixbar axilxbar wbxbar axis2mm aximm2s axidma axim2wbsp aximrd2wbsp
+.PHONY: aximwr2wbsp axixclk axiperf demoaxi demofull easyaxil sfifo skidbuffer
+.PHONY: axilgpio
+.PHONY: axilsafety axisafety wbsafety axivfifo axil2apb apbslave
+.PHONY: axisrandom axisswitch axisbroadcast axispacker
+
+axim2wbsp: $(VDIRFB)/Vwbm2axisp__ALL.a $(IVCHECK)/axim2wbsp
+axim2wbsp: $(VDIRFB)/Vaxim2wbsp__ALL.a
+wbm2axilite: $(VDIRFB)/Vwbm2axilite__ALL.a $(IVCHECK)/wbm2axilite
+axilrd2wbsp: $(VDIRFB)/Vaxilrd2wbsp__ALL.a $(IVCHECK)/axilrd2wbsp
+axilwr2wbsp: $(VDIRFB)/Vaxilwr2wbsp__ALL.a $(IVCHECK)/axilwr2wbsp
+axlite2wbsp: $(VDIRFB)/Vaxlite2wbsp__ALL.a $(IVCHECK)/axlite2wbsp
+axiempty: $(VDIRFB)/Vaxiempty__ALL.a $(IVCHECK)/axiempty
+axilempty: $(VDIRFB)/Vaxilempty__ALL.a $(IVCHECK)/axilempty
+axilgpio: $(VDIRFB)/Vaxilgpio__ALL.a $(IVCHECK)/axilgpio
+axivcamera: $(VDIRFB)/Vaxivcamera__ALL.a $(IVCHECK)/axivcamera
+axivdisplay: $(VDIRFB)/Vaxivdisplay__ALL.a $(IVCHECK)/axivdisplay
+axivfifo: $(VDIRFB)/Vaxivfifo__ALL.a $(IVCHECK)/axivfifo
+axixbar: $(VDIRFB)/Vaxixbar__ALL.a $(IVCHECK)/axixbar
+axilxbar: $(VDIRFB)/Vaxilxbar__ALL.a $(IVCHECK)/axilxbar
+wbxbar: $(VDIRFB)/Vwbxbar__ALL.a $(IVCHECK)/wbxbar
+axis2mm: $(VDIRFB)/Vaxis2mm__ALL.a $(IVCHECK)/axis2mm
+aximm2s: $(VDIRFB)/Vaximm2s__ALL.a $(IVCHECK)/aximm2s
+axidma: $(VDIRFB)/Vaxidma__ALL.a $(IVCHECK)/axidma
+axim2wbsp: $(VDIRFB)/Vaxim2wbsp__ALL.a $(IVCHECK)/axim2wbsp
+aximrd2wbsp: $(VDIRFB)/Vaximrd2wbsp__ALL.a $(IVCHECK)/aximrd2wbsp
+aximwr2wbsp: $(VDIRFB)/Vaximwr2wbsp__ALL.a $(IVCHECK)/aximwr2wbsp
+axiperf: $(VDIRFB)/Vaxiperf__ALL.a $(IVCHECK)/axiperf
+axixclk: $(VDIRFB)/Vaxixclk__ALL.a $(IVCHECK)/axixclk
+demoaxi: $(VDIRFB)/Vdemoaxi__ALL.a $(IVCHECK)/demoaxi
+demofull: $(VDIRFB)/Vdemofull__ALL.a $(IVCHECK)/demofull
+easyaxil: $(VDIRFB)/Veasyaxil__ALL.a $(IVCHECK)/easyaxil
+sfifo: $(VDIRFB)/Vsfifo__ALL.a $(IVCHECK)/sfifo
+skidbuffer: $(VDIRFB)/Vskidbuffer__ALL.a $(IVCHECK)/skidbuffer
+axisafety: $(VDIRFB)/Vaxisafety__ALL.a $(IVCHECK)/axisafety
+axilsafety: $(VDIRFB)/Vaxilsafety__ALL.a $(IVCHECK)/axilsafety
+wbsafety: $(VDIRFB)/Vwbsafety__ALL.a $(IVCHECK)/wbsafety
+axil2apb: $(VDIRFB)/Vaxil2apb__ALL.a $(IVCHECK)/axil2apb
+apbslave: $(VDIRFB)/Vapbslave__ALL.a $(IVCHECK)/apbslave
+axisbroadcast: $(VDIRFB)/Vaxisbroadcast__ALL.a $(IVCHECK)/axisbroadcast
+axispacker: $(VDIRFB)/Vaxispacker__ALL.a $(IVCHECK)/axispacker
+axisrandom: $(VDIRFB)/Vaxisrandom__ALL.a $(IVCHECK)/axisrandom
+axisswitch: $(VDIRFB)/Vaxisswitch__ALL.a $(IVCHECK)/axisswitch
+
+testwb: wbsafety wbm2axilite wbxbar sfifo wbsafety wbxbar wbsafety
+testaxi: axim2wbsp axixbar axixclk demofull axiempty
+testaxi: aximrd2wbsp axim2wbsp aximwr2wbsp axisafety
+testaxi: axis2mm aximm2s axidma axivfifo axivdisplay axivcamera
+testaxil: axilrd2wbsp axilwr2wbsp axlite2wbsp axilxbar demoaxi easyaxil
+testaxil: skidbuffer axiperf axilempty axilgpio
+testapb: axil2apb apbslave
+testaxis: axisbroadcast axisswitch axisrandom axispacker
+
+.PHONY: wbm2axisp
+wbm2axisp: $(VDIRFB)/Vwbm2axisp__ALL.a
+$(VDIRFB)/Vwbm2axisp__ALL.a: $(VDIRFB)/Vwbm2axisp.h $(VDIRFB)/Vwbm2axisp.cpp
+$(VDIRFB)/Vwbm2axisp__ALL.a: $(VDIRFB)/Vwbm2axisp.mk
+$(VDIRFB)/Vwbm2axisp.h $(VDIRFB)/Vwbm2axisp.cpp $(VDIRFB)/Vwbm2axisp.mk: wbm2axisp.v
+
+.PHONY: wbm2axilite
+wbm2axilite: $(VDIRFB)/Vwbm2axilite__ALL.a
+$(VDIRFB)/Vwbm2axilite__ALL.a: $(VDIRFB)/Vwbm2axilite.h $(VDIRFB)/Vwbm2axilite.cpp
+$(VDIRFB)/Vwbm2axilite__ALL.a: $(VDIRFB)/Vwbm2axilite.mk
+$(VDIRFB)/Vwbm2axilite.h $(VDIRFB)/Vwbm2axilite.cpp $(VDIRFB)/Vwbm2axilite.mk: wbm2axilite.v
+
+.PHONY: axilrd2wbsp
+axilrd2wbsp: $(VDIRFB)/Vaxilrd2wbsp__ALL.a
+$(VDIRFB)/Vaxilrd2wbsp__ALL.a: $(VDIRFB)/Vaxilrd2wbsp.h $(VDIRFB)/Vaxilrd2wbsp.cpp
+$(VDIRFB)/Vaxilrd2wbsp__ALL.a: $(VDIRFB)/Vaxilrd2wbsp.mk
+$(VDIRFB)/Vaxilrd2wbsp.h $(VDIRFB)/Vaxilrd2wbsp.cpp $(VDIRFB)/Vaxilrd2wbsp.mk: axilrd2wbsp.v
+
+.PHONY: axilwr2wbsp
+axilwr2wbsp: $(VDIRFB)/Vaxilwr2wbsp__ALL.a
+$(VDIRFB)/Vaxilwr2wbsp__ALL.a: $(VDIRFB)/Vaxilwr2wbsp.h $(VDIRFB)/Vaxilwr2wbsp.cpp
+$(VDIRFB)/Vaxilwr2wbsp__ALL.a: $(VDIRFB)/Vaxilwr2wbsp.mk
+$(VDIRFB)/Vaxilwr2wbsp.h $(VDIRFB)/Vaxilwr2wbsp.cpp $(VDIRFB)/Vaxilwr2wbsp.mk: axilwr2wbsp.v
+
+$(VDIRFB)/Vaxlite2wbsp__ALL.a: $(VDIRFB)/Vaxlite2wbsp.h $(VDIRFB)/Vaxlite2wbsp.cpp
+$(VDIRFB)/Vaxlite2wbsp__ALL.a: $(VDIRFB)/Vaxlite2wbsp.mk
+$(VDIRFB)/Vaxlite2wbsp.h $(VDIRFB)/Vaxlite2wbsp.cpp $(VDIRFB)/Vaxlite2wbsp.mk: axlite2wbsp.v
+
+$(VDIRFB)/Vaxiempty__ALL.a: $(VDIRFB)/Vaxiempty.h $(VDIRFB)/Vaxiempty.cpp
+$(VDIRFB)/Vaxiempty__ALL.a: $(VDIRFB)/Vaxiempty.mk
+$(VDIRFB)/Vaxiempty.h $(VDIRFB)/Vaxiempty.cpp $(VDIRFB)/Vaxiempty.mk: axiempty.v skidbuffer.v
+
+$(VDIRFB)/Vaxilempty__ALL.a: $(VDIRFB)/Vaxilempty.h $(VDIRFB)/Vaxilempty.cpp
+$(VDIRFB)/Vaxilempty__ALL.a: $(VDIRFB)/Vaxilempty.mk
+$(VDIRFB)/Vaxilempty.h $(VDIRFB)/Vaxilempty.cpp $(VDIRFB)/Vaxilempty.mk: axilempty.v skidbuffer.v
+
+$(VDIRFB)/Vaxilgpio__ALL.a: $(VDIRFB)/Vaxilgpio.h $(VDIRFB)/Vaxilgpio.cpp
+$(VDIRFB)/Vaxilgpio__ALL.a: $(VDIRFB)/Vaxilgpio.mk
+$(VDIRFB)/Vaxilgpio.h $(VDIRFB)/Vaxilgpio.cpp $(VDIRFB)/Vaxilgpio.mk: axilgpio.v skidbuffer.v
+
+$(VDIRFB)/Vaxim2wbsp__ALL.a: $(VDIRFB)/Vaxim2wbsp.h $(VDIRFB)/Vaxim2wbsp.cpp
+$(VDIRFB)/Vaxim2wbsp__ALL.a: $(VDIRFB)/Vaxim2wbsp.mk
+$(VDIRFB)/Vaxim2wbsp.h $(VDIRFB)/Vaxim2wbsp.cpp $(VDIRFB)/Vaxim2wbsp.mk: \
+ axim2wbsp.v aximrd2wbsp.v aximwr2wbsp.v wbarbiter.v
+
+$(VDIRFB)/Vaxivfifo__ALL.a: $(VDIRFB)/Vaxivfifo.h $(VDIRFB)/Vaxivfifo.cpp
+$(VDIRFB)/Vaxivfifo__ALL.a: $(VDIRFB)/Vaxivfifo.mk
+$(VDIRFB)/Vaxivfifo.h $(VDIRFB)/Vaxivfifo.cpp $(VDIRFB)/Vaxivfifo.mk: \
+ axivfifo.v skidbuffer.v sfifo.v
+
+$(VDIRFB)/Vaxivcamera__ALL.a: $(VDIRFB)/Vaxivcamera.h $(VDIRFB)/Vaxivcamera.cpp
+$(VDIRFB)/Vaxivcamera__ALL.a: $(VDIRFB)/Vaxivcamera.mk
+$(VDIRFB)/Vaxivcamera.h $(VDIRFB)/Vaxivcamera.cpp $(VDIRFB)/Vaxivcamera.mk: \
+ axivcamera.v skidbuffer.v sfifo.v
+
+$(VDIRFB)/Vaxivdisplay__ALL.a: $(VDIRFB)/Vaxivdisplay.h $(VDIRFB)/Vaxivdisplay.cpp
+$(VDIRFB)/Vaxivdisplay__ALL.a: $(VDIRFB)/Vaxivdisplay.mk
+$(VDIRFB)/Vaxivdisplay.h $(VDIRFB)/Vaxivdisplay.cpp $(VDIRFB)/Vaxivdisplay.mk: \
+ axivdisplay.v skidbuffer.v sfifo.v
+
+$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v
+ $(VERILATOR) $(VFLAGS) $*.v
+
+$(VDIRFB)/V%__ALL.a: $(VDIRFB)/V%.mk
+ cd $(VDIRFB); make -f V$*.mk
+
+##
+## Run Icarus Verilog (iverilog) on each of these cores for a lint check
+##
+$(IVCHECK)/axi2axilite: axi2axilite.v sfifo.v skidbuffer.v axi_addr.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axil2axis: axil2axis.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axildouble: axildouble.v sfifo.v skidbuffer.v addrdecode.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilrd2wbsp: axilrd2wbsp.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilsafety: axilsafety.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilsingle: axilsingle.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilwr2wbsp: axilwr2wbsp.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilxbar: axilxbar.v skidbuffer.v addrdecode.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axidma: axidma.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/aximm2s: aximm2s.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axim2wbsp: axim2wbsp.v sfifo.v skidbuffer.v aximrd2wbsp.v aximwr2wbsp.v axi_addr.v wbarbiter.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/aximrd2wbsp: aximrd2wbsp.v sfifo.v skidbuffer.v axi_addr.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/aximwr2wbsp: aximwr2wbsp.v sfifo.v skidbuffer.v axi_addr.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axiperf: axiperf.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axisafety: axisafety.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axis2mm: axis2mm.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axivcamera: axivcamera.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axivdisplay: axivdisplay.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axivfifo: axivfifo.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axixbar: axixbar.v skidbuffer.v addrdecode.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axixclk: axixclk.v afifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axlite2wbsp: axlite2wbsp.v axilrd2wbsp.v axilwr2wbsp.v wbarbiter.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axiempty: axiempty.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilempty: axilempty.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axilgpio: axilgpio.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/demoaxi: demoaxi.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/demofull: demofull.v axi_addr.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/easyaxil: easyaxil.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/sfifo: sfifo.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/skidbuffer: skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/wbm2axilite: wbm2axilite.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/wbsafety: wbsafety.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/wbxbar: wbxbar.v skidbuffer.v addrdecode.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axil2apb: axil2apb.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/apbslave: apbslave.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axisbroadcast: axisbroadcast.v sfifo.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axispacker: axispacker.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axisrandom: axisrandom.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+$(IVCHECK)/axisswitch: axisswitch.v skidbuffer.v
+ $(mk-ivcheck)
+ $(IVERILOG) -g2012 $^ -o $@
+
+define mk-ivcheck
+ @bash -c "if [ ! -e $(IVCHECK) ]; then mkdir -p $(IVCHECK); fi"
+endef
+
+.PHONY: clean
+clean:
+ rm -rf $(VDIRFB)/*.mk
+ rm -rf $(VDIRFB)/*.cpp
+ rm -rf $(VDIRFB)/*.h
+ rm -rf $(VDIRFB)/
+ rm -rf $(IVCHECK)/
diff --git a/rtl/wb2axip/README.md b/rtl/wb2axip/README.md
new file mode 100644
index 0000000..6aa99a8
--- /dev/null
+++ b/rtl/wb2axip/README.md
@@ -0,0 +1,175 @@
+## Demo designs
+
+- [Demonstration AXI4-lite slave](demoaxi.v)
+- [Demonstration APB slave](apbslave.v)
+- [Demonstration AXI4(Full) slave](demofull.v)
+ -- [AXI Addr](axi_addr.v) is a helper core used for calculating the "next" address in a burst sequence. It's based upon [the algorithm discussed here](https://zipcpu.com/blog/2019/04/27/axi-addr.html).
+- [A simplified AXI-lite slave](easyaxil.v)
+- [A basic AXI-lite GPIO controller](axilgpio.v)
+
+- [Skidbuffer](skidbuffer.v)
+
+## Demo AXI-stream designs
+
+- [AXIS Broadcast](axisbroadcast.v): Accepts one stream input, duplicates that
+ stream to an arbitrary number of output streams
+- [AXISPACKER](axispacker.v): Removes NULL bytes from an AXI Stream
+- [AXISRANDOM](axisrandom.v): Generates a pseudorandom AXI-Stream output
+- [AXISSWITCH](axisswitch.v): Switches a stream from among many input streams,
+ with an AXI-lite control input for switching between them.
+
+## Crossbars
+
+See [this post for a discussion of these
+crossbars](https://zipcpu.com/blog/2019/07/17/crossbars.html).
+
+- [AXI4](axixbar.v)
+- [AXI4-Lite](axilxbar.v)
+- [Wishbone](wbxbar.v)
+
+These cores rely not only on the [skidbuffer](skidbuffer.v) listed above, but
+also upon a separate [address decoder](addrdecode.v) that is common to all of
+them.
+
+All three cores are supported by the (dev branch of)
+[AutoFPGA](https://github.com/ZipCPU/autofpga).
+
+## Data movers/DMA engines
+
+- [AXIMM2S](aximm2s.v). Supports unaligned transfers, but only fully aligned
+ stream words.
+- [AXIS2MM](axis2mm.v). Doesn't yet support unaligned transfers. It may
+ eventually, but it will also only ever support full word transfers
+ through the stream.
+- [AXIDMA](axidma.v). Supports unaligned transfers.
+- [AXISGDMA](axisgdma.v). A scatter-gather DMA implementation. Performs DMA
+ operations based upon an external, bus-fetched, table containing the details
+ of multiple DMA operations to be done.
+ -- [AXILFETCH](axilfetch.v)--A ZipCPU instruction fetch module required by the Scatter-Gather engine to fetch tables from memory.
+ -- [AXISGFSM](axisgfsm.v)--The FSM that reads instructions (i.e. table entries) from the fetch routine, and issues instructions to the [AXIDMA](axidma.v).
+- [AXIVFIFO](axivfifo.v). A virtual FIFO, using an external AXI device for
+ memory backing--perhaps even an SDRAM. It doesn't really matter--it just
+ needs to be AXI.
+- [AXIVCAMERA](axivcamera.v). Writes a video stream to a memory frame buffer.
+- [AXIVDISPLAY](axivdisplay.v). Reads a frame buffer from memory to generate
+ a continuous AXI-stream video source output.
+
+- [Synchronous FIFO](sfifo.v)
+- [Synchronous FIFO with threshold](sfifothresh.v)
+
+## Bus Bridges
+
+- [AXI to AXI-lite](axi2axilite.v). Supports 100% throughput even across burst
+ boundaries, unlike other (similar) bridges of this type you might come across.
+
+- [AXI-lite to AXI](axilite2axi.v). A "no-cost" bridge.
+
+- [AXI-lite to AXI stream](axil2axis.v)
+
+- [AXI-Lite to Wishbone](axlite2wbsp.v)
+
+ -- [Read side](axilrd2wbsp.v)
+
+ -- [Write side](axilwr2wbsp.v)
+
+ -- A following (optional) [arbiter](wbarbiter.v) will connect read and write sides together into the same WB bus. Alternatively, each of the two sides can be submitted separately into a [WB Crossbar](wbxbar.v).
+
+- [AXI-Lite to APB](axil2apb.v)
+
+- [AXI4 Master to Wishbone](axim2wbsp.v)
+
+ -- [Read side](aximrd2wbsp.v)
+
+ -- [AXI4 Master to Wishbone](aximwr2wbsp.v)
+
+- [Wishbone classic to pipelined](wbc2pipeline.v)
+
+- [Wishbone pipelined to classic](wbp2classic.v)
+
+- [Wishbone master to AXI4-Litejk slave](wbm2axilite.v)
+
+- [Wishbone master to AXI4 slave](wbm2axisp.v)
+
+ This core, together with its sister core, [migsdram](migsdram.v), form the
+ basis for my approach to accessing DDR3 SDRAM memory through an AXI
+ interface using Xilinx's MIG controller. Other than the lag in going
+ through the bridge, this solution works quite well--achieving full (nearly
+ 100%) bus throughput when loaded.
+
+- [AXI3 to AXI4](axi32axi.v). See the internal documentation of the
+ [AXI3 write deinterleaver](axi3reorder.v) for the details of the write
+ deinterleaving algorithm.
+
+- The AXI4 to AXI3 bridge is still missing.
+ This will be more difficult than its [AXI3 to AXI4 sister](axi32axi.v) above,
+ since it will need to break apart large AXI4 burst requests into smaller AXI3
+ bursts, sort of like the [AXI4 to AXI4-lite bridge](axi2axilite.v) does.
+
+## AXI Simplifiers
+
+These follow from the discussion in [this article about how to simplify
+a bus interconnect](https://zipcpu.com/zipcpu/2019/08/30/subbus.html) into
+something allowing easier integration of multiple slaves into a design. They
+work by aggregating the signaling logic across many slaves together. See the
+headers for each of the cores for the specifics of the standard subsets
+they support.
+
+- AXI Single, the companion to the [AXI Double](axidouble.v) core has not yet
+ been written. The design for it should be nearly identical. The big
+ difference between single and double AXI slaves is that single slaves can have
+ only one address, and that address must always be available for reading.
+
+- [AXI Double](axidouble.v).
+
+- [AXI-Lite Single](axilsingle.v)
+
+- [AXI-Lite Double](axildouble.v)
+
+## Firewalls
+
+The goal of these firewall(s) is to create a core that, when placed between the
+bus master and slave, will guarantee that the slave responds appropriately to
+the bus master---even in the presence of a broken slave. If the slave is
+broken, the firewall will raise a flag that can then be used to trigger a
+logic analyzer of some type so you can dig deeper into what's going on.
+
+As a bonus, the firewall may also have the capability of resetting the
+downstream core upon any error, so that it might be reintegrated later into
+the rest of the design for additional testing.
+
+- [AXI-lite](axilsafety.v)
+
+- [AXI4](axisafety.v)
+
+- [AXI-stream](axissafety.v)
+
+- [Wishbone](wbsafety.v)
+
+## Performance Measurement
+
+- [AXIPERF](axiperf.v) -- Useful for measuring Latency and Throughput in an
+AXI bus. Has an AXI-lite interface, and an AXI4 monitor interface. Monitors
+the AXI4 bus, and measures key performance statistics. Statistics may be read
+and cleared via the AXI-lite interface.
+
+## Empty slaves
+
+These are used to simplify interconnect generation. When there are no slaves
+connected to an interconnect, the interconnect generator can automatically
+connect one of these slaves (depending on the protocol) and be guaranteed
+that the protocol will still be followed. All requests, however, will return
+bus errors.
+
+- [AXIEMPTY](axiempty.v) - the AXI4 empty slave
+
+- [AXILEMPTY](axilempty.v) - the AXI4-lite empty slave
+
+## Clock domain crossing bridges
+
+- [Wishbone cross-clock domains](wbxclk.v)
+
+- [AXI4 cross-clock domains](axixclk.v)
+
+- [APB cross-clock domains](apbxclk.v)
+
+- [Asynchronous FIFO](afifo.v) -- support function
diff --git a/rtl/wb2axip/addrdecode.v b/rtl/wb2axip/addrdecode.v
new file mode 100644
index 0000000..2f3cac6
--- /dev/null
+++ b/rtl/wb2axip/addrdecode.v
@@ -0,0 +1,461 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: addrdecode.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Supports bus crossbars by answering the question, which slave
+// does the current address need to be routed to? Requests are
+// pipelined using valid/!stall handshaking. For those familiar with
+// AXI, READY=!STALL. The outgoing stream is identical to the incoming
+// one, save for the (new) o_decode field. This is a bitmask containing
+// one bit for each slave that the request might be routed to, and one
+// extra bit to indicate no slaves matched.
+//
+// The keys to the operation of this module are found in the two
+// parameters, SLAVE_ADDR and SLAVE_MASK.
+//
+// SLAVE_ADDR specifies the address of the slave in question. It's a large
+// array, with one address (i.e. one set of AW bits) for each
+// potential slave address region.
+//
+// SLAVE_MASK specifies which of the bits in SLAVE_ADDR need to match in
+// order to route a request to a that slave.
+//
+// It's important to guarantee that no two slaves will ever map to the
+// same address, and likewise any given slave may only map to a single
+// address region.
+//
+// Incidentally, the algorithm forces all slaves to have an address
+// aligned with their memory size. Hence a 2GB memory must have an
+// address (range) of either 0-2GB, 2GB-4GB, 4GB-6GB, etc. However, a
+// second slave having only 8kB of memory may be placed at 0-8kB,
+// 8-16kB, 16-24kB, etc. For logic minimization purposes, it is often to
+// the advantage of the bus compositor to minimize the number of mask
+// bits, and hence 8kB slaves may be aliased to many places in memory.
+// Bus composition and address assignment, however, are both outside of
+// the scope of the operation of this module.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module addrdecode #(
+ // {{{
+ parameter NS=8,
+ parameter AW = 32, DW=32+32/8+1+1,
+ //
+ // SLAVE_ADDR contains address assignments for each of the
+ // various slaves we are adjudicating between.
+ parameter [NS*AW-1:0] SLAVE_ADDR = {
+ { 3'b111, {(AW-3){1'b0}} },
+ { 3'b110, {(AW-3){1'b0}} },
+ { 3'b101, {(AW-3){1'b0}} },
+ { 3'b100, {(AW-3){1'b0}} },
+ { 3'b011, {(AW-3){1'b0}} },
+ { 3'b010, {(AW-3){1'b0}} },
+ { 4'b0010, {(AW-4){1'b0}} },
+ { 4'b0000, {(AW-4){1'b0}} }},
+ //
+ // SLAVE_MASK contains a mask of those address bits in
+ // SLAVE_ADDR which are relevant. It shall be true that if
+ // !SLAVE_MASK[k] then !SLAVE_ADDR[k], for any bits of k
+ parameter [NS*AW-1:0] SLAVE_MASK = (NS <= 1) ? 0
+ : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }},
+ {(2){ 4'b1111, {(AW-4){1'b0}} }} },
+ //
+ // ACCESS_ALLOWED is a bit-wise mask indicating which slaves
+ // may get access to the bus. If ACCESS_ALLOWED[slave] is true,
+ // then a master can connect to the slave via this method. This
+ // parameter is primarily here to support AXI (or other similar
+ // buses) which may have separate accesses for both read and
+ // write. By using this, a read-only slave can be connected,
+ // which would also naturally create an error on any attempt to
+ // write to it.
+ parameter [NS-1:0] ACCESS_ALLOWED = -1,
+ //
+ // If OPT_REGISTERED is set, address decoding will take an extra
+ // clock, and will register the results of the decoding
+ // operation.
+ parameter [0:0] OPT_REGISTERED = 0,
+ //
+ // If OPT_LOWPOWER is set, then whenever the output is not
+ // valid, any respective data linse will also be forced to zero
+ // in an effort to minimize power.
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ //
+ input wire i_valid,
+ output reg o_stall,
+ input wire [AW-1:0] i_addr,
+ input wire [DW-1:0] i_data,
+ //
+ output reg o_valid,
+ input wire i_stall,
+ output reg [NS:0] o_decode,
+ output reg [AW-1:0] o_addr,
+ output reg [DW-1:0] o_data
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ //
+ // OPT_NONESEL controls whether or not the address lines are fully
+ // proscribed, or whether or not a "no-slave identified" slave should
+ // be created. To avoid a "no-slave selected" output, slave zero must
+ // have no mask bits set (and therefore no address bits set), and it
+ // must also allow access.
+ localparam [0:0] OPT_NONESEL = (!ACCESS_ALLOWED[0])
+ || (SLAVE_MASK[AW-1:0] != 0);
+ //
+ wire [NS:0] request;
+ reg [NS-1:0] prerequest;
+ integer iM;
+ // }}}
+
+ // prerequest
+ // {{{
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ prerequest[iM] = (((i_addr ^ SLAVE_ADDR[iM*AW +: AW])
+ &SLAVE_MASK[iM*AW +: AW])==0)
+ &&(ACCESS_ALLOWED[iM]);
+ // }}}
+
+ // request
+ // {{{
+ generate if (OPT_NONESEL)
+ begin : NO_DEFAULT_REQUEST
+ // {{{
+ reg [NS-1:0] r_request;
+
+ // Need to create a slave to describe when nothing is selected
+ //
+ always @(*)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ r_request[iM] = i_valid && prerequest[iM];
+ if (!OPT_NONESEL && (NS > 1 && |prerequest[NS-1:1]))
+ r_request[0] = 1'b0;
+ end
+
+ assign request[NS-1:0] = r_request;
+ // }}}
+ end else if (NS == 1)
+ begin : SINGLE_SLAVE
+ // {{{
+ assign request[0] = i_valid;
+ // }}}
+ end else begin : GENERAL_CASE
+ // {{{
+ reg [NS-1:0] r_request;
+
+ always @(*)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ r_request[iM] = i_valid && prerequest[iM];
+ if (!OPT_NONESEL && (NS > 1 && |prerequest[NS-1:1]))
+ r_request[0] = 1'b0;
+ end
+
+ assign request[NS-1:0] = r_request;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // request[NS]
+ // {{{
+ generate if (OPT_NONESEL)
+ begin : GENERATE_NONSEL_SLAVE
+ reg r_request_NS, r_none_sel;
+
+ always @(*)
+ begin
+ // Let's assume nothing's been selected, and then check
+ // to prove ourselves wrong.
+ //
+ // Note that none_sel will be considered an error
+ // condition in the follow-on processing. Therefore
+ // it's important to clear it if no request is pending.
+ r_none_sel = i_valid && (prerequest == 0);
+ //
+ // request[NS] indicates a request for a non-existent
+ // slave. A request that should (eventually) return a
+ // bus error
+ //
+ r_request_NS = r_none_sel;
+ end
+
+ assign request[NS] = r_request_NS;
+ end else begin : NO_NONESEL_SLAVE
+ assign request[NS] = 1'b0;
+ end endgenerate
+ // }}}
+
+ // o_valid, o_addr, o_data, o_decode, o_stall
+ // {{{
+ generate if (OPT_REGISTERED)
+ begin : GEN_REG_OUTPUTS
+
+ // o_valid
+ // {{{
+ initial o_valid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_valid <= 0;
+ else if (!o_stall)
+ o_valid <= i_valid;
+ // }}}
+
+ // o_addr, o_data
+ // {{{
+ initial o_addr = 0;
+ initial o_data = 0;
+ always @(posedge i_clk)
+ if (i_reset && OPT_LOWPOWER)
+ begin
+ o_addr <= 0;
+ o_data <= 0;
+ end else if ((!o_valid || !i_stall)
+ && (i_valid || !OPT_LOWPOWER))
+ begin
+ o_addr <= i_addr;
+ o_data <= i_data;
+ end else if (OPT_LOWPOWER && !i_stall)
+ begin
+ o_addr <= 0;
+ o_data <= 0;
+ end
+ // }}}
+
+ // o_decode
+ // {{{
+ initial o_decode = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_decode <= 0;
+ else if ((!o_valid || !i_stall)
+ && (i_valid || !OPT_LOWPOWER))
+ o_decode <= request;
+ else if (OPT_LOWPOWER && !i_stall)
+ o_decode <= 0;
+ // }}}
+
+ // o_stall
+ // {{{
+ always @(*)
+ o_stall = (o_valid && i_stall);
+ // }}}
+ end else begin : GEN_COMBINATORIAL_OUTPUTS
+
+ always @(*)
+ begin
+ o_valid = i_valid;
+ o_stall = i_stall;
+ o_addr = i_addr;
+ o_data = i_data;
+
+ o_decode = request;
+ end
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+`ifdef VERILATOR
+ // Can't declare the clock as unused for formal,
+ // lest it not be recognized as *the* clock
+ i_clk,
+`endif
+ i_reset };
+ // verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1;
+
+ reg [AW+DW-1:0] f_idata;
+ always @(*)
+ f_idata = { i_addr, i_data };
+
+`ifdef ADDRDECODE
+ always @(posedge i_clk)
+ if (!f_past_valid)
+ assume(i_reset);
+`else
+ always @(posedge i_clk)
+ if (!f_past_valid)
+ assert(i_reset);
+
+`endif // ADDRDECODE
+ always @(posedge i_clk)
+ if (OPT_REGISTERED && (!f_past_valid || $past(i_reset)))
+ begin
+ assert(!o_valid);
+ assert(o_decode == 0);
+ end else if ($past(o_valid && i_stall) && OPT_REGISTERED)
+ begin
+ assert($stable(o_addr));
+ assert($stable(o_decode));
+ assert($stable(o_data));
+ end
+
+ // If the output is ever valid, there must be at least one
+ // decoded output
+ always @(*)
+ assert(o_valid == (o_decode != 0));
+
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ if (o_decode[iM])
+ begin
+ // The address must match
+ assert((((o_addr ^ SLAVE_ADDR[iM*AW +: AW])
+ & SLAVE_MASK[iM*AW +: AW])==0)
+ && ACCESS_ALLOWED[iM]);
+ //
+ // And nothing else must match
+ assert(o_decode == (1<<iM));
+ end
+
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ if (!ACCESS_ALLOWED[iM])
+ assert(!o_decode[iM]);
+
+ // LOWPOWER check
+ // {{{
+ generate if (OPT_LOWPOWER && OPT_REGISTERED)
+ begin
+ always @(*)
+ if (!o_valid)
+ begin
+ assert(o_addr == 0);
+ assert(o_decode == 0);
+ assert(o_data == 0);
+ end
+ end endgenerate
+ // }}}
+
+ //
+ // The output decoded value may only ever have one value high,
+ // never more--i.e. $onehot0
+ // {{{
+`ifdef VERIFIC
+ always @(*)
+ assert($onehot0(request));
+`else
+ reg onehot_request;
+ always @(*)
+ begin
+ onehot_request = 0;
+ for(iM=0; iM<NS+1; iM=iM+1)
+ if ((request ^ (1<<iM))==0)
+ onehot_request = 1;
+ end
+
+ always @(*)
+ if (request != 0)
+ assert(onehot_request);
+`endif
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Make sure all addresses are reachable
+ //
+ reg [NS:0] f_reached;
+
+ always @(posedge i_clk)
+ cover(i_valid);
+
+ always @(posedge i_clk)
+ cover(o_valid);
+
+ always @(posedge i_clk)
+ cover(o_valid && !i_stall);
+
+ initial f_reached = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ f_reached = 0;
+ else if (o_valid)
+ f_reached = f_reached | o_decode;
+
+ generate if (!OPT_NONESEL && ACCESS_ALLOWED[0]
+ && SLAVE_MASK == 0 && NS == 1)
+ begin
+
+ always @(*)
+ cover(f_reached[0]);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $stable(o_valid))
+ assert($stable(o_decode));
+
+ end else begin
+
+ always @(*)
+ cover(&f_reached);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $stable(o_valid))
+ cover($changed(o_decode));
+
+ end endgenerate
+ // }}}
+`endif // FORMAL
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/afifo.v b/rtl/wb2axip/afifo.v
new file mode 100644
index 0000000..99af9cc
--- /dev/null
+++ b/rtl/wb2axip/afifo.v
@@ -0,0 +1,801 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: afifo.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A basic asynchronous FIFO.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module afifo #(
+ // {{{
+ // LGFIFO is the log based-two of the number of entries
+ // in the FIFO, log_2(fifo size)
+ parameter LGFIFO = 3,
+ //
+ // WIDTH is the number of data bits in each entry
+ parameter WIDTH = 16,
+ //
+ // NFF is the number of flip flops used to cross clock domains.
+ // 2 is a minimum. Some applications appreciate the better
+ parameter NFF = 2,
+ //
+ // This core can either write on the positive edge of the clock
+ // or the negative edge. Set WRITE_ON_POSEDGE (the default)
+ // to write on the positive edge of the clock.
+ parameter [0:0] WRITE_ON_POSEDGE = 1'b1,
+ //
+ // Many logic elements can read from memory asynchronously.
+ // This burdens any following logic. By setting
+ // OPT_REGISTER_READS, we force all reads to be synchronous and
+ // not burdened by any logic. You can spare a clock of latency
+ // by clearing this register.
+ parameter [0:0] OPT_REGISTER_READS = 1'b1
+`ifdef FORMAL
+ // F_OPT_DATA_STB
+ // {{{
+ // In the formal proof, F_OPT_DATA_STB includes a series of
+ // assumptions associated with a data strobe I/O pin--things
+ // like a discontinuous clock--just to make sure the core still
+ // works in those circumstances
+ , parameter [0:0] F_OPT_DATA_STB = 1'b1
+ // }}}
+`endif
+ // }}}
+ ) (
+ // {{{
+ //
+ // The (incoming) write data interface
+ input wire i_wclk, i_wr_reset_n, i_wr,
+ input wire [WIDTH-1:0] i_wr_data,
+ output reg o_wr_full,
+ //
+ // The (incoming) write data interface
+ input wire i_rclk, i_rd_reset_n, i_rd,
+ output reg [WIDTH-1:0] o_rd_data,
+ output reg o_rd_empty
+`ifdef FORMAL
+ , output reg [LGFIFO:0] f_fill
+`endif
+ // }}}
+ );
+
+ // Register/net declarations
+ // {{{
+ // MSB = most significant bit of the FIFO address vector. It's
+ // just short-hand for LGFIFO, and won't work any other way.
+ localparam MSB = LGFIFO;
+ //
+ reg [WIDTH-1:0] mem [(1<<LGFIFO)-1:0];
+ reg [LGFIFO:0] rd_addr, wr_addr,
+ rd_wgray, wr_rgray;
+ wire [LGFIFO:0] next_rd_addr, next_wr_addr;
+ reg [LGFIFO:0] rgray, wgray;
+ (* ASYNC_REG = "TRUE" *) reg [(LGFIFO+1)*(NFF-1)-1:0]
+ rgray_cross, wgray_cross;
+ wire wclk;
+ reg [WIDTH-1:0] lcl_rd_data;
+ reg lcl_read, lcl_rd_empty;
+ // }}}
+
+ // wclk - Write clock generation
+ // {{{
+ generate if (WRITE_ON_POSEDGE)
+ begin : GEN_POSEDGE_WRITES
+
+ assign wclk = i_wclk;
+
+ end else begin : GEN_NEGEDGE_WRITES
+
+ assign wclk = !i_wclk;
+
+ end endgenerate
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write to and read from memory
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // wr_addr, wgray
+ // {{{
+ assign next_wr_addr = wr_addr + 1;
+ always @(posedge wclk or negedge i_wr_reset_n)
+ if (!i_wr_reset_n)
+ begin
+ wr_addr <= 0;
+ wgray <= 0;
+ end else if (i_wr && !o_wr_full)
+ begin
+ wr_addr <= next_wr_addr;
+ wgray <= next_wr_addr ^ (next_wr_addr >> 1);
+ end
+ // }}}
+
+ // Write to memory
+ // {{{
+ always @(posedge wclk)
+ if (i_wr && !o_wr_full)
+ mem[wr_addr[LGFIFO-1:0]] <= i_wr_data;
+ // }}}
+
+ // rd_addr, rgray
+ // {{{
+ assign next_rd_addr = rd_addr + 1;
+ always @(posedge i_rclk or negedge i_rd_reset_n)
+ if (!i_rd_reset_n)
+ begin
+ rd_addr <= 0;
+ rgray <= 0;
+ end else if (lcl_read && !lcl_rd_empty)
+ begin
+ rd_addr <= next_rd_addr;
+ rgray <= next_rd_addr ^ (next_rd_addr >> 1);
+ end
+ // }}}
+
+ // Read from memory
+ // {{{
+ always @(*)
+ lcl_rd_data = mem[rd_addr[LGFIFO-1:0]];
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cross clock domains
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // read pointer -> wr_rgray
+ // {{{
+ always @(posedge wclk or negedge i_wr_reset_n)
+ if (!i_wr_reset_n)
+ { wr_rgray, rgray_cross } <= 0;
+ else
+ { wr_rgray, rgray_cross } <= { rgray_cross, rgray };
+ // }}}
+
+ // write pointer -> rd_wgray
+ // {{{
+ always @(posedge i_rclk or negedge i_rd_reset_n)
+ if (!i_rd_reset_n)
+ { rd_wgray, wgray_cross } <= 0;
+ else
+ { rd_wgray, wgray_cross } <= { wgray_cross, wgray };
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Flag generation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ o_wr_full = (wr_rgray == { ~wgray[MSB:MSB-1], wgray[MSB-2:0] });
+
+ always @(*)
+ lcl_rd_empty = (rd_wgray == rgray);
+
+ // o_rd_empty, o_rd_data
+ // {{{
+ generate if (OPT_REGISTER_READS)
+ begin : GEN_REGISTERED_READ
+ // {{{
+ always @(*)
+ lcl_read = (o_rd_empty || i_rd);
+
+ always @(posedge i_rclk or negedge i_rd_reset_n)
+ if (!i_rd_reset_n)
+ o_rd_empty <= 1'b1;
+ else if (lcl_read)
+ o_rd_empty <= lcl_rd_empty;
+
+ always @(posedge i_rclk)
+ if (lcl_read)
+ o_rd_data <= lcl_rd_data;
+ // }}}
+ end else begin : GEN_COMBINATORIAL_FLAGS
+ // {{{
+ always @(*)
+ lcl_read = i_rd;
+
+ always @(*)
+ o_rd_empty = lcl_rd_empty;
+
+ always @(*)
+ o_rd_data = lcl_rd_data;
+ // }}}
+ end endgenerate
+ // }}}
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Start out with some register/net/macro declarations, f_past_valid,etc
+ // {{{
+`ifdef AFIFO
+`define ASSERT assert
+`define ASSUME assume
+`else
+`define ASSERT assert
+`define ASSUME assert
+`endif
+
+ (* gclk *) reg gbl_clk;
+ reg f_past_valid_gbl, f_past_valid_rd,
+ f_rd_in_reset, f_wr_in_reset;
+ reg [WIDTH-1:0] past_rd_data, past_wr_data;
+ reg past_wr_reset_n, past_rd_reset_n,
+ past_rd_empty, past_wclk, past_rclk, past_rd;
+ reg [(LGFIFO+1)*(NFF-1)-1:0] f_wcross, f_rcross;
+ reg [LGFIFO:0] f_rd_waddr, f_wr_raddr;
+ reg [LGFIFO:0] f_rdcross_fill [NFF-1:0];
+ reg [LGFIFO:0] f_wrcross_fill [NFF-1:0];
+
+
+ initial f_past_valid_gbl = 1'b0;
+ always @(posedge gbl_clk)
+ f_past_valid_gbl <= 1'b1;
+
+ initial f_past_valid_rd = 1'b0;
+ always @(posedge i_rclk)
+ f_past_valid_rd <= 1'b1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Reset checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial f_wr_in_reset = 1'b1;
+ always @(posedge wclk or negedge i_wr_reset_n)
+ if (!i_wr_reset_n)
+ f_wr_in_reset <= 1'b1;
+ else
+ f_wr_in_reset <= 1'b0;
+
+ initial f_rd_in_reset = 1'b1;
+ always @(posedge i_rclk or negedge i_rd_reset_n)
+ if (!i_rd_reset_n)
+ f_rd_in_reset <= 1'b1;
+ else
+ f_rd_in_reset <= 1'b0;
+
+ //
+ // Resets are ...
+ // 1. Asserted always initially, and ...
+ always @(*)
+ if (!f_past_valid_gbl)
+ begin
+ `ASSUME(!i_wr_reset_n);
+ `ASSUME(!i_rd_reset_n);
+ end
+
+ // 2. They only ever become active together
+ always @(*)
+ if (past_wr_reset_n && !i_wr_reset_n)
+ `ASSUME(!i_rd_reset_n);
+
+ always @(*)
+ if (past_rd_reset_n && !i_rd_reset_n)
+ `ASSUME(!i_wr_reset_n);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Synchronous signal assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge gbl_clk)
+ begin
+ past_wr_reset_n <= i_wr_reset_n;
+ past_rd_reset_n <= i_rd_reset_n;
+
+ past_wclk <= wclk;
+ past_rclk <= i_rclk;
+
+ past_rd <= i_rd;
+ past_rd_data <= lcl_rd_data;
+ past_wr_data <= i_wr_data;
+
+ past_rd_empty<= lcl_rd_empty;
+ end
+
+ //
+ // Read side may be assumed to be synchronous
+ always @(*)
+ if (f_past_valid_gbl && i_rd_reset_n && (past_rclk || !i_rclk))
+ // i.e. if (!$rose(i_rclk))
+ `ASSUME(i_rd == past_rd);
+
+ always @(*)
+ if (f_past_valid_rd && !f_rd_in_reset && !lcl_rd_empty
+ &&(past_rclk || !i_rclk))
+ begin
+ `ASSERT(lcl_rd_data == past_rd_data);
+ `ASSERT(lcl_rd_empty == past_rd_empty);
+ end
+
+
+ generate if (F_OPT_DATA_STB)
+ begin
+
+ always @(posedge gbl_clk)
+ `ASSUME(!o_wr_full);
+
+ always @(posedge gbl_clk)
+ if (!i_wr_reset_n)
+ `ASSUME(!i_wclk);
+
+ always @(posedge gbl_clk)
+ `ASSUME(i_wr == i_wr_reset_n);
+
+ always @(posedge gbl_clk)
+ if ($changed(i_wr_reset_n))
+ `ASSUME($stable(wclk));
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Fill checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ f_fill = wr_addr - rd_addr;
+
+ always @(*)
+ if (!f_wr_in_reset)
+ `ASSERT(f_fill <= { 1'b1, {(MSB){1'b0}} });
+
+ always @(*)
+ if (wr_addr == rd_addr)
+ `ASSERT(lcl_rd_empty);
+
+ always @(*)
+ if ((!f_wr_in_reset && !f_rd_in_reset)
+ && wr_addr == { ~rd_addr[MSB], rd_addr[MSB-1:0] })
+ `ASSERT(o_wr_full);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // f_wr_in_reset -- write logic is in its reset state
+ // {{{
+ always @(*)
+ if (f_wr_in_reset)
+ begin
+ `ASSERT(wr_addr == 0);
+ `ASSERT(wgray_cross == 0);
+
+ `ASSERT(rd_addr == 0);
+ `ASSERT(rgray_cross == 0);
+ `ASSERT(rd_wgray == 0);
+
+ `ASSERT(lcl_rd_empty);
+ `ASSERT(!o_wr_full);
+ end
+ // }}}
+
+ // f_rd_in_reset -- read logic is in its reset state
+ // {{{
+ always @(*)
+ if (f_rd_in_reset)
+ begin
+ `ASSERT(rd_addr == 0);
+ `ASSERT(rgray_cross == 0);
+ `ASSERT(rd_wgray == 0);
+
+ `ASSERT(lcl_rd_empty);
+ end
+ // }}}
+
+ // f_wr_raddr -- a read address to match the gray values
+ // {{{
+ always @(posedge wclk or negedge i_wr_reset_n)
+ if (!i_wr_reset_n)
+ { f_wr_raddr, f_rcross } <= 0;
+ else
+ { f_wr_raddr, f_rcross } <= { f_rcross, rd_addr };
+ // }}}
+
+ // f_rd_waddr -- a write address to match the gray values
+ // {{{
+ always @(posedge i_rclk or negedge i_rd_reset_n)
+ if (!i_rd_reset_n)
+ { f_rd_waddr, f_wcross } <= 0;
+ else
+ { f_rd_waddr, f_wcross } <= { f_wcross, wr_addr };
+ // }}}
+
+ integer k;
+
+ // wgray check
+ // {{{
+ always @(*)
+ `ASSERT((wr_addr ^ (wr_addr >> 1)) == wgray);
+ // }}}
+
+ // wgray_cross check
+ // {{{
+ always @(*)
+ for(k=0; k<NFF-1; k=k+1)
+ `ASSERT((f_wcross[k*(LGFIFO+1) +: LGFIFO+1]
+ ^ (f_wcross[k*(LGFIFO+1)+: LGFIFO+1]>>1))
+ == wgray_cross[k*(LGFIFO+1) +: LGFIFO+1]);
+ // }}}
+
+ // rgray check
+ // {{{
+ always @(*)
+ `ASSERT((rd_addr ^ (rd_addr >> 1)) == rgray);
+ // }}}
+
+ // rgray_cross check
+ // {{{
+ always @(*)
+ for(k=0; k<NFF-1; k=k+1)
+ `ASSERT((f_rcross[k*(LGFIFO+1) +: LGFIFO+1]
+ ^ (f_rcross[k*(LGFIFO+1) +: LGFIFO+1]>>1))
+ == rgray_cross[k*(LGFIFO+1) +: LGFIFO+1]);
+ // }}}
+
+ // wr_rgray
+ // {{{
+ always @(*)
+ `ASSERT((f_wr_raddr ^ (f_wr_raddr >> 1)) == wr_rgray);
+ // }}}
+
+ // rd_wgray
+ // {{{
+ always @(*)
+ `ASSERT((f_rd_waddr ^ (f_rd_waddr >> 1)) == rd_wgray);
+ // }}}
+
+ // f_rdcross_fill
+ // {{{
+ always @(*)
+ for(k=0; k<NFF-1; k=k+1)
+ f_rdcross_fill[k] = f_wcross[k*(LGFIFO+1) +: LGFIFO+1]
+ - rd_addr;
+
+ always @(*)
+ f_rdcross_fill[NFF-1] = f_rd_waddr - rd_addr;
+
+ always @(*)
+ for(k=0; k<NFF; k=k+1)
+ `ASSERT(f_rdcross_fill[k] <= { 1'b1, {(LGFIFO){1'b0}} });
+
+ always @(*)
+ for(k=1; k<NFF; k=k+1)
+ `ASSERT(f_rdcross_fill[k] <= f_rdcross_fill[k-1]);
+ always @(*)
+ `ASSERT(f_rdcross_fill[0] <= f_fill);
+ // }}}
+
+ // f_wrcross_fill
+ // {{{
+ always @(*)
+ for(k=0; k<NFF-1; k=k+1)
+ f_wrcross_fill[k] = wr_addr -
+ f_rcross[k*(LGFIFO+1) +: LGFIFO+1];
+
+ always @(*)
+ f_wrcross_fill[NFF-1] = wr_addr - f_wr_raddr;
+
+ always @(*)
+ for(k=0; k<NFF; k=k+1)
+ `ASSERT(f_wrcross_fill[k] <= { 1'b1, {(LGFIFO){1'b0}} });
+
+ always @(*)
+ for(k=1; k<NFF; k=k+1)
+ `ASSERT(f_wrcross_fill[k] >= f_wrcross_fill[k-1]);
+ always @(*)
+ `ASSERT(f_wrcross_fill[0] >= f_fill);
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Clock generation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Here's the challenge: if we use $past with any of our clocks, such
+ // as to determine stability or any such, the proof takes forever, and
+ // we need to guarantee a minimum number of transitions within the
+ // depth of the proof. If, on the other hand, we build our own $past
+ // primitives--we can then finish much faster and be successful on
+ // any depth of proof.
+
+ // pre_xclk is what the clock will become on the next global clock edge.
+ // By using it here, we can check things @(*) instead of
+ // @(posedge gbl_clk). Further, we can check $rose(pre_xclk) (or $fell)
+ // and essentially check things @(*) but while using @(global_clk).
+ // In other words, we can transition on @(posedge gbl_clk), but stay
+ // in sync with the data--rather than being behind by a clock.
+ // now_xclk is what the clock is currently.
+ //
+ (* anyseq *) reg pre_wclk, pre_rclk;
+ reg now_wclk, now_rclk;
+ always @(posedge gbl_clk)
+ begin
+ now_wclk <= pre_wclk;
+ now_rclk <= pre_rclk;
+ end
+
+ always @(*)
+ begin
+ assume(i_wclk == now_wclk);
+ assume(i_rclk == now_rclk);
+ end
+
+ always @(posedge gbl_clk)
+ assume(i_rclk == $past(pre_rclk));
+
+ // Assume both clocks start idle
+ // {{{
+ always @(*)
+ if (!f_past_valid_gbl)
+ begin
+ assume(!pre_wclk && !wclk);
+ assume(!pre_rclk && !i_rclk);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract check --- the twin write test
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Tracking register declarations
+ // {{{
+ reg [WIDTH-1:0] f_first, f_next;
+ (* anyconst *) reg [LGFIFO:0] f_addr;
+ reg [LGFIFO:0] f_next_addr;
+ reg f_first_in_fifo, f_next_in_fifo;
+ reg [LGFIFO:0] f_to_first, f_to_next;
+ reg [1:0] f_state;
+
+ always @(*)
+ f_next_addr = f_addr + 1;
+ // }}}
+
+ // distance_to*, *_in_fifo
+ // {{{
+ always @(*)
+ begin
+ f_to_first = f_addr - rd_addr;
+
+ f_first_in_fifo = 1'b1;
+ if ((f_to_first >= f_fill)||(f_fill == 0))
+ f_first_in_fifo = 1'b0;
+
+ if (mem[f_addr] != f_first)
+ f_first_in_fifo = 1'b0;
+
+ //
+ // Check the second item
+ //
+
+ f_to_next = f_next_addr - rd_addr;
+ f_next_in_fifo = 1'b1;
+ if ((f_to_next >= f_fill)||(f_fill == 0))
+ f_next_in_fifo = 1'b0;
+
+ if (mem[f_next_addr] != f_next)
+ f_next_in_fifo = 1'b0;
+ end
+ // }}}
+
+ // f_state -- generate our state variable
+ // {{{
+ initial f_state = 0;
+ always @(posedge gbl_clk)
+ if (!i_wr_reset_n)
+ f_state <= 0;
+ else case(f_state)
+ 2'b00: if (($rose(pre_wclk))&& i_wr && !o_wr_full &&(wr_addr == f_addr))
+ begin
+ f_state <= 2'b01;
+ f_first <= i_wr_data;
+ end
+ 2'b01: if ($rose(pre_rclk)&& lcl_read && rd_addr == f_addr)
+ f_state <= 2'b00;
+ else if ($rose(pre_wclk) && i_wr && !o_wr_full )
+ begin
+ f_state <= 2'b10;
+ f_next <= i_wr_data;
+ end
+ 2'b10: if ($rose(pre_rclk) && lcl_read && !lcl_rd_empty && rd_addr == f_addr)
+ f_state <= 2'b11;
+ 2'b11: if ($rose(pre_rclk) && lcl_read && !lcl_rd_empty && rd_addr == f_next_addr)
+ f_state <= 2'b00;
+ endcase
+ // }}}
+
+ // f_state invariants
+ // {{{
+ always @(*)
+ if (i_wr_reset_n) case(f_state)
+ 2'b00: begin end
+ 2'b01: begin
+ `ASSERT(f_first_in_fifo);
+ `ASSERT(wr_addr == f_next_addr);
+ `ASSERT(f_fill >= 1);
+ end
+ 2'b10: begin
+ `ASSERT(f_first_in_fifo);
+ `ASSERT(f_next_in_fifo);
+ if (!lcl_rd_empty && (rd_addr == f_addr))
+ `ASSERT(lcl_rd_data == f_first);
+ `ASSERT(f_fill >= 2);
+ end
+ 2'b11: begin
+ `ASSERT(rd_addr == f_next_addr);
+ `ASSERT(f_next_in_fifo);
+ `ASSERT(f_fill >= 1);
+ if (!lcl_rd_empty)
+ `ASSERT(lcl_rd_data == f_next);
+ end
+ endcase
+ // }}}
+
+ generate if (OPT_REGISTER_READS)
+ begin
+ reg past_o_rd_empty;
+
+ always @(posedge gbl_clk)
+ past_o_rd_empty <= o_rd_empty;
+
+ always @(posedge gbl_clk)
+ if (f_past_valid_gbl && i_rd_reset_n)
+ begin
+ if ($past(!o_rd_empty && !i_rd && i_rd_reset_n))
+ `ASSERT($stable(o_rd_data));
+ end
+
+ always @(posedge gbl_clk)
+ if (!f_rd_in_reset && i_rd_reset_n && i_rclk && !past_rclk)
+ begin
+ if (past_o_rd_empty)
+ `ASSERT(o_rd_data == past_rd_data);
+ if (past_rd)
+ `ASSERT(o_rd_data == past_rd_data);
+ end
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Prove that we can read and write the FIFO
+ // {{{
+ always @(*)
+ if (i_wr_reset_n && i_rd_reset_n)
+ begin
+ cover(o_rd_empty);
+ cover(!o_rd_empty);
+ cover(f_state == 2'b01);
+ cover(f_state == 2'b10);
+ cover(f_state == 2'b11);
+ cover(&f_fill[MSB-1:0]);
+
+ cover(i_rd);
+ cover(i_rd && !o_rd_empty);
+ end
+ // }}}
+
+`ifdef AFIFO
+ generate if (!F_OPT_DATA_STB)
+ begin : COVER_FULL
+ // {{{
+ reg cvr_full;
+
+ initial cvr_full = 1'b0;
+ always @(posedge gbl_clk)
+ if (!i_wr_reset_n)
+ cvr_full <= 1'b0;
+ else if (o_wr_full)
+ cvr_full <= 1'b1;
+
+
+ always @(*)
+ if (f_past_valid_gbl && i_wr_reset_n)
+ begin
+ cover(o_wr_full);
+ cover(o_rd_empty && cvr_full);
+ cover(o_rd_empty && f_fill == 0 && cvr_full);
+ end
+ // }}}
+ end else begin : COVER_NEARLY_FULL
+ // {{{
+ reg cvr_nearly_full;
+
+ initial cvr_nearly_full = 1'b0;
+ always @(posedge gbl_clk)
+ if (!i_wr_reset_n)
+ cvr_nearly_full <= 1'b0;
+ else if (f_fill == { 1'b0, {(LGFIFO){1'b1} }})
+ cvr_nearly_full <= 1'b1;
+
+
+ always @(*)
+ if (f_past_valid_gbl && i_wr_reset_n)
+ begin
+ cover(f_fill == { 1'b0, {(LGFIFO){1'b1} }});
+ cover(cvr_nearly_full && i_wr_reset_n);
+ cover(o_rd_empty && cvr_nearly_full);
+ cover(o_rd_empty && f_fill == 0 && cvr_nearly_full);
+ end
+ // }}}
+ end endgenerate
+`endif
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/apbslave.v b/rtl/wb2axip/apbslave.v
new file mode 100644
index 0000000..e3ba027
--- /dev/null
+++ b/rtl/wb2axip/apbslave.v
@@ -0,0 +1,229 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: apbslave.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Just a simple demonstration APB slave
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// }}}
+`default_nettype none
+//
+module apbslave #(
+ // {{{
+ parameter C_APB_ADDR_WIDTH = 12,
+ parameter C_APB_DATA_WIDTH = 32,
+ localparam AW = C_APB_ADDR_WIDTH,
+ localparam DW = C_APB_DATA_WIDTH,
+ localparam APBLSB = $clog2(C_APB_DATA_WIDTH)-3
+ // }}}
+ ) (
+ // {{{
+ input wire PCLK, PRESETn,
+ input wire PSEL,
+ input wire PENABLE,
+ output reg PREADY,
+ input wire [AW-1:0] PADDR,
+ input wire PWRITE,
+ input wire [DW-1:0] PWDATA,
+ input wire [DW/8-1:0] PWSTRB,
+ input wire [2:0] PPROT,
+ output reg [DW-1:0] PRDATA,
+ output wire PSLVERR
+ // }}}
+ );
+
+ // Register declarations
+ // {{{
+ // Just our demonstration "memory" here
+ reg [DW-1:0] mem [0:(1<<(AW-APBLSB))-1];
+ integer ik;
+ // }}}
+
+ // PREADY
+ // {{{
+ initial PREADY = 1'b0;
+ always @(posedge PCLK)
+ if (!PRESETn)
+ PREADY <= 1'b0;
+ else if (PSEL && !PENABLE)
+ PREADY <= 1'b1;
+ else
+ PREADY <= 1'b0;
+ // }}}
+
+ // mem writes
+ // {{{
+ always @(posedge PCLK)
+ if (PRESETn && PSEL && !PENABLE && PWRITE)
+ begin
+ for(ik=0; ik<DW/8; ik=ik+1)
+ if (PWSTRB[ik])
+ mem[PADDR[AW-1:APBLSB]][8*ik +: 8] <= PWDATA[8*ik +: 8];
+ end
+ // }}}
+
+ // PRDATA, memory reads
+ // {{{
+ always @(posedge PCLK)
+ if (PSEL && !PENABLE && !PWRITE)
+ PRDATA <= mem[PADDR[AW-1:APBLSB]];
+ // }}}
+
+ // PSLVERR -- unused in this design, and so kept at zero
+ // {{{
+ assign PSLVERR = 1'b0;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, PADDR[APBLSB-1:0], PPROT };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ fapb_slave #(.AW(C_APB_ADDR_WIDTH), .DW(C_APB_DATA_WIDTH),
+ .F_OPT_MAXSTALL(1)
+ ) fapb (PCLK, PRESETn,
+ PSEL, PENABLE, PREADY, PADDR, PWRITE, PWDATA, PWSTRB,
+ PPROT, PRDATA, PSLVERR);
+
+ always @(*)
+ if (PRESETn && PSEL && PENABLE)
+ assert(PREADY);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [AW-1:0] f_addr;
+ reg [DW-1:0] f_data;
+
+`ifndef FORMAL
+ initial for(ik=0; ik<(1<<AW); ik=ik+1)
+ mem[ik] = 0;
+`endif
+ always @(*)
+ if (!PRESETn)
+ assume(mem[f_addr[AW-1:APBLSB]] == f_data);
+
+ initial f_data = 0;
+ always @(posedge PCLK)
+ if (PRESETn && PSEL && !PENABLE && PWRITE && PADDR[AW-1:APBLSB] == f_addr[AW-1:APBLSB])
+ begin
+ for(ik=0; ik<DW/8; ik=ik+1)
+ if (PWSTRB[ik])
+ f_data[ik*8 +: 8] <= PWDATA[ik*8 +: 8];
+ end
+
+ always @(posedge PCLK)
+ if (PSEL && PENABLE && PREADY && !PWRITE && PADDR[AW-1:APBLSB] == f_addr[AW-1:APBLSB])
+ assert(PRDATA == f_data);
+
+ always @(*)
+ assert(f_data == mem[f_addr[AW-1:APBLSB]]);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ reg [2:0] cvr_reads, cvr_writes, cvr_seq;
+
+ initial cvr_writes = 0;
+ always @(posedge PCLK)
+ if (!PRESETn)
+ cvr_writes <= 0;
+ else if (PSEL && PENABLE && PREADY && PWRITE && !cvr_writes[2])
+ cvr_writes <= cvr_writes + 1;
+
+ initial cvr_reads = 0;
+ always @(posedge PCLK)
+ if (!PRESETn)
+ cvr_reads <= 0;
+ else if (PSEL && PENABLE && PREADY && !PWRITE && !cvr_reads[2])
+ cvr_reads <= cvr_reads + 1;
+
+ always @(*)
+ cover(cvr_writes[2]);
+
+ always @(*)
+ cover(cvr_reads[2]);
+
+ initial cvr_seq = 0;
+ always @(posedge PCLK)
+ if (!PRESETn)
+ cvr_seq <= 0;
+ else if (PSEL && PENABLE && PREADY && !PWRITE && PADDR == f_addr)
+ begin
+ if (cvr_seq == 0 && PRDATA == 32'h12345678)
+ cvr_seq[0] <= 1'b1;
+ if (cvr_seq[0] && PRDATA == 32'h87654321)
+ cvr_seq[1] <= 1'b1;
+ if (cvr_seq[1] && PRDATA == 32'h0)
+ cvr_seq[2] <= 1'b1;
+ end
+
+ always @(*)
+ if (PRESETn)
+ begin
+ cover(cvr_seq[0]);
+ cover(cvr_seq[1]);
+ cover(cvr_seq[2]);
+ end
+
+ always @(*)
+ cover(PRESETn && !PSEL && !PENABLE && cvr_seq[2]);
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/apbxclk.v b/rtl/wb2axip/apbxclk.v
new file mode 100644
index 0000000..831675b
--- /dev/null
+++ b/rtl/wb2axip/apbxclk.v
@@ -0,0 +1,610 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: apbxclk.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose:
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// }}}
+`default_nettype none
+//
+module apbxclk #(
+ // {{{
+ parameter C_APB_ADDR_WIDTH = 12,
+ parameter C_APB_DATA_WIDTH = 32,
+ parameter [0:0] OPT_REGISTERED = 1'b0,
+ localparam AW = C_APB_ADDR_WIDTH,
+ localparam DW = C_APB_DATA_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire S_APB_PCLK, S_PRESETn,
+ input wire S_APB_PSEL,
+ input wire S_APB_PENABLE,
+ output reg S_APB_PREADY,
+ input wire [AW-1:0] S_APB_PADDR,
+ input wire S_APB_PWRITE,
+ input wire [DW-1:0] S_APB_PWDATA,
+ input wire [DW/8-1:0] S_APB_PWSTRB,
+ input wire [2:0] S_APB_PPROT,
+ output wire [DW-1:0] S_APB_PRDATA,
+ output wire S_APB_PSLVERR,
+ //
+ input wire M_APB_PCLK,
+ output reg M_PRESETn,
+ output reg M_APB_PSEL,
+ output reg M_APB_PENABLE,
+ input wire M_APB_PREADY,
+ output wire [AW-1:0] M_APB_PADDR,
+ output wire M_APB_PWRITE,
+ output wire [DW-1:0] M_APB_PWDATA,
+ output wire [DW/8-1:0] M_APB_PWSTRB,
+ output wire [2:0] M_APB_PPROT,
+ input wire [DW-1:0] M_APB_PRDATA,
+ input wire M_APB_PSLVERR
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ reg reset_pipe, full_reset_pipe, full_reset;
+
+ reg s_request, s_tfr_request;
+ reg m_request, m_request_pipe;
+
+ reg m_ack;
+ reg s_ack, s_ack_pipe;
+ reg [DW-1:0] m_prdata;
+ reg m_pslverr;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Synchronize resets
+ // {{{
+ always @(posedge M_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ { M_PRESETn, reset_pipe } <= 0;
+ else
+ { M_PRESETn, reset_pipe } <= { reset_pipe, 1'b1 };
+
+ always @(posedge S_APB_PCLK or negedge M_PRESETn)
+ if (!M_PRESETn)
+ { full_reset, full_reset_pipe } <= 0;
+ else
+ { full_reset, full_reset_pipe } <= { full_reset_pipe, 1'b1 };
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Move the request across clock domains
+ // {{{
+
+ // s_request
+ // {{{
+ // Step 1: Register the request
+ // Always register anything before moving cross clock domains
+ always @(posedge S_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ s_request <= 0;
+ else if (S_APB_PREADY)
+ s_request <= 0;
+ else if (S_APB_PSEL && !S_APB_PENABLE)
+ s_request <= 1;
+ // }}}
+
+ // s_tfr_request
+ // {{{
+ // Step 2: Forward requests--but only when the downstream handshake
+ // is ready to accept them
+ always @(posedge S_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ s_tfr_request <= 0;
+ else if (S_APB_PREADY)
+ s_tfr_request <= 0;
+ else if ((S_APB_PSEL && !S_APB_PENABLE) || s_request)
+ s_tfr_request <= (full_reset && !s_ack);
+ // }}}
+
+ // m_request, m_request_pipe -- 3. Capture the request in new clk domain
+ // {{{
+ always @(posedge M_APB_PCLK or negedge M_PRESETn)
+ if (!M_PRESETn)
+ { m_request, m_request_pipe } <= 0;
+ else
+ { m_request, m_request_pipe }
+ <= { m_request_pipe, s_tfr_request };
+ // }}}
+
+ // M_APB_PSEL, M_APB_PENABLE -- 4. Forward the request downstream
+ // {{{
+ always @(posedge M_APB_PCLK or negedge M_PRESETn)
+ if (!M_PRESETn)
+ begin
+ M_APB_PSEL <= 0;
+ M_APB_PENABLE <= 1'b0;
+ end else begin
+ M_APB_PSEL <= m_request && !m_ack
+ && (!M_APB_PSEL || !M_APB_PENABLE || !M_APB_PREADY);
+ M_APB_PENABLE <= M_APB_PSEL && (!M_APB_PSEL || !M_APB_PENABLE || !M_APB_PREADY);
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Move the response back across clock domains
+ // {{{
+
+ // m_ack
+ // {{{
+ always @(posedge M_APB_PCLK or negedge M_PRESETn)
+ if (!M_PRESETn)
+ m_ack <= 0;
+ else if (m_request && !m_ack && M_APB_PSEL && M_APB_PREADY && M_APB_PENABLE)
+ m_ack <= 1'b1;
+ else if (!m_request)
+ m_ack <= 1'b0;
+ // }}}
+
+ // s_ack, s_ack_pipe
+ // {{{
+ always @(posedge S_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ { s_ack, s_ack_pipe } <= 0;
+ else
+ { s_ack, s_ack_pipe } <= { s_ack_pipe, m_ack };
+ // }}}
+
+ // S_APB_PREADY
+ // {{{
+ always @(posedge S_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ S_APB_PREADY <= 0;
+ else
+ S_APB_PREADY <= !S_APB_PREADY && s_tfr_request && s_ack;
+ // }}}
+
+ // m_prdata
+ // {{{
+ always @(posedge M_APB_PCLK)
+ if (M_APB_PSEL && M_APB_PREADY)
+ m_prdata <= M_APB_PRDATA;
+ // }}}
+
+ // m_pslverr
+ // {{{
+ always @(posedge M_APB_PCLK or negedge M_PRESETn)
+ if (!M_PRESETn)
+ m_pslverr <= 0;
+ else if (M_APB_PSEL && M_APB_PREADY)
+ m_pslverr <= M_APB_PSLVERR;
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Handle the ancillary (data bearing) signals
+ // {{{
+
+ generate if (OPT_REGISTERED)
+ begin : GEN_REGISTERS
+ // {{{
+ // Register all results into the new domain when
+ // crossing clock domains
+ reg [AW-1:0] m_paddr;
+ reg m_pwrite;
+ reg [DW-1:0] m_pwdata;
+ reg [DW/8-1:0] m_pwstrb;
+ reg [2:0] m_pprot;
+
+ reg [DW-1:0] s_prdata;
+ reg s_pslverr;
+
+ always @(posedge M_APB_PCLK)
+ if (m_request && !m_ack)
+ begin
+ m_paddr <= S_APB_PADDR;
+ m_pwrite <= S_APB_PWRITE;
+ m_pwdata <= S_APB_PWDATA;
+ m_pwstrb <= S_APB_PWSTRB;
+ m_pprot <= S_APB_PPROT;
+ end
+
+ assign M_APB_PADDR = m_paddr;
+ assign M_APB_PWRITE = m_pwrite;
+ assign M_APB_PWDATA = m_pwdata;
+ assign M_APB_PWSTRB = m_pwstrb;
+ assign M_APB_PPROT = m_pprot;
+
+ always @(posedge S_APB_PCLK or negedge S_PRESETn)
+ if (!S_PRESETn)
+ s_pslverr <= 1'b0;
+ else if (s_tfr_request && s_ack
+ && (!S_APB_PREADY || !S_APB_PENABLE))
+ s_pslverr <= m_pslverr;
+ else
+ s_pslverr <= 0;
+
+ always @(posedge S_APB_PCLK)
+ if (s_request && s_ack)
+ begin
+ s_prdata <= m_prdata;
+ end else begin
+ s_prdata <= 0;
+ end
+
+ assign S_APB_PRDATA = s_prdata;
+ assign S_APB_PSLVERR = s_pslverr;
+ // }}}
+ end else begin : NO_REGISTERING
+ // {{{
+ // Results will be stable whenever PSEL is true. There's
+ // really no *need* to register them
+
+ assign M_APB_PADDR = S_APB_PADDR;
+ assign M_APB_PWRITE = S_APB_PWRITE;
+ assign M_APB_PWDATA = S_APB_PWDATA;
+ assign M_APB_PWSTRB = S_APB_PWSTRB;
+ assign M_APB_PPROT = S_APB_PPROT;
+
+ assign S_APB_PRDATA = m_prdata;
+ assign S_APB_PSLVERR = m_pslverr && S_APB_PREADY;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ (* gclk *) reg gbl_clk;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ fapb_slave #(
+ // {{{
+ .AW(C_APB_ADDR_WIDTH), .DW(C_APB_DATA_WIDTH),
+ .F_OPT_MAXSTALL(0), .F_OPT_ASYNC_RESET(1'b1),
+ .F_OPT_SLVERR(1'b1)
+ // }}}
+ ) fslv (
+ // {{{
+ .PCLK(S_APB_PCLK), .PRESETn(S_PRESETn),
+ .PSEL(S_APB_PSEL),
+ .PENABLE(S_APB_PENABLE),
+ .PREADY(S_APB_PREADY),
+ .PADDR(S_APB_PADDR),
+ .PWRITE(S_APB_PWRITE), .PWDATA(S_APB_PWDATA),
+ .PWSTRB(S_APB_PWSTRB),
+ .PPROT(S_APB_PPROT),
+ .PRDATA(S_APB_PRDATA), .PSLVERR(S_APB_PSLVERR)
+ // }}}
+ );
+
+ fapb_master #(
+ // {{{
+ .AW(C_APB_ADDR_WIDTH), .DW(C_APB_DATA_WIDTH),
+ .F_OPT_MAXSTALL(0), .F_OPT_ASYNC_RESET(1'b1),
+ .F_OPT_SLVERR(1'b1)
+ // }}}
+ ) fmas (
+ // {{{
+ .PCLK(M_APB_PCLK), .PRESETn(M_PRESETn),
+ .PSEL(M_APB_PSEL), .PENABLE(M_APB_PENABLE),
+ .PREADY(M_APB_PREADY), .PADDR(M_APB_PADDR),
+ .PWRITE(M_APB_PWRITE), .PWDATA(M_APB_PWDATA),
+ .PWSTRB(M_APB_PWSTRB),
+ .PPROT(M_APB_PPROT),
+ .PRDATA(M_APB_PRDATA), .PSLVERR(M_APB_PSLVERR)
+ // }}}
+ );
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Clock stability
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg f_past_valid_gbl;
+
+ initial f_past_valid_gbl <= 1'b0;
+ always @(posedge gbl_clk)
+ f_past_valid_gbl <= 1'b1;
+
+ always @(*)
+ if (!f_past_valid_gbl)
+ assume(!S_PRESETn);
+
+ always @(posedge gbl_clk)
+ if (!$rose(S_APB_PCLK))
+ begin
+ assume(!$rose(S_PRESETn));
+ assume($stable(S_APB_PSEL));
+ assume($stable(S_APB_PENABLE));
+ assume($stable(S_APB_PADDR));
+ assume($stable(S_APB_PWRITE));
+ assume($stable(S_APB_PWDATA));
+ assume($stable(S_APB_PWSTRB));
+ assume($stable(S_APB_PPROT));
+ //
+ if (f_past_valid_gbl && S_PRESETn)
+ begin
+ assert($stable(S_APB_PREADY));
+ if (OPT_REGISTERED)
+ begin
+ assert($stable(S_APB_PRDATA));
+ assert($stable(S_APB_PSLVERR));
+ end else if ($past(S_APB_PREADY) && S_APB_PREADY)
+ begin
+ assert($stable(S_APB_PRDATA));
+ assert($stable(S_APB_PSLVERR));
+ end
+ end
+ end
+
+ always @(posedge gbl_clk)
+ if (!$rose(M_APB_PCLK))
+ begin
+ if (f_past_valid_gbl)
+ begin
+ assert(!$rose(M_PRESETn));
+ end
+
+ if (f_past_valid_gbl && M_PRESETn)
+ begin
+ assert($stable(M_APB_PSEL));
+ assert($stable(M_APB_PENABLE));
+ if (OPT_REGISTERED)
+ begin
+ assert($stable(M_APB_PADDR));
+ assert($stable(M_APB_PWRITE));
+ assert($stable(M_APB_PWDATA));
+ assert($stable(M_APB_PWSTRB));
+ assert($stable(M_APB_PPROT));
+ end else if ($past(M_APB_PSEL) && M_APB_PSEL)
+ begin
+ assert($stable(M_APB_PADDR));
+ assert($stable(M_APB_PWRITE));
+ assert($stable(M_APB_PWDATA));
+ assert($stable(M_APB_PWSTRB));
+ assert($stable(M_APB_PPROT));
+ end
+ end
+ //
+ assume($stable(M_APB_PREADY));
+ assume($stable(M_APB_PRDATA));
+ assume($stable(M_APB_PSLVERR));
+ end
+
+ always @(posedge gbl_clk)
+ if ($past(S_APB_PSEL && !S_APB_PREADY) && $past(S_PRESETn) && S_PRESETn)
+ begin
+ assume($stable(S_APB_PSEL));
+ assume($stable(S_APB_PADDR));
+ assume($stable(S_APB_PWRITE));
+ assume($stable(S_APB_PWDATA));
+ assume($stable(S_APB_PWSTRB));
+ assume($stable(S_APB_PPROT));
+ end
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction invariants
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ case({ S_PRESETn, reset_pipe, M_PRESETn, full_reset_pipe, full_reset })
+ 5'b0_00_00: begin end
+ 5'b1_00_00: begin end
+ 5'b1_10_00: begin end
+ 5'b1_11_00: begin end
+ 5'b1_11_10: begin end
+ 5'b1_11_11: begin end
+ default: assert(0);
+ endcase
+
+ always @(*)
+ casez({ s_request, s_tfr_request, m_request_pipe, m_request, m_ack, s_ack_pipe, s_ack })
+ 7'b00_00_000: begin end
+ 7'b10_00_000: begin end
+ 7'b11_00_000: begin end
+ 7'b11_10_000: begin end
+ 7'b11_11_000: begin end
+ 7'b11_11_100: begin end
+ 7'b11_11_110: begin end
+ 7'b11_11_111: begin end
+ 7'b?0_11_111: begin end
+ 7'b?0_01_111: begin end
+ 7'b?0_00_111: begin end
+ 7'b?0_00_011: begin end
+ 7'b?0_00_001: begin end
+ 7'b?0_00_000: begin end
+ default: assert(0);
+ endcase
+
+ always @(posedge S_APB_PCLK)
+ if (s_request && S_PRESETn)
+ assert(S_APB_PSEL && S_APB_PENABLE);
+
+ always @(posedge gbl_clk)
+ if (!s_tfr_request && S_PRESETn)
+ assert(!M_APB_PSEL);
+
+ always @(posedge gbl_clk)
+ if (M_APB_PSEL)
+ assert(m_request && !m_ack);
+
+ always @(posedge gbl_clk)
+ if (S_PRESETn && (m_ack || s_ack_pipe || s_ack || S_APB_PREADY))
+ assert(!M_APB_PSEL);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ (* anyconst *) reg fnvr_check;
+ (* anyconst *) reg [3+DW/8+DW:0] fnvr_write;
+ (* anyconst *) reg [AW-1:0] fnvr_addr;
+ (* anyconst *) reg [1+DW-1:0] fnvr_return;
+
+ always @(posedge gbl_clk)
+ if (S_APB_PSEL && fnvr_check)
+ begin
+ assume({ S_APB_PPROT, S_APB_PWSTRB, S_APB_PWDATA } != fnvr_write);
+ assume(S_APB_PADDR != fnvr_addr);
+ end
+
+ always @(posedge gbl_clk)
+ if (M_APB_PSEL && fnvr_check && M_PRESETn)
+ begin
+ assert({ M_APB_PPROT, M_APB_PWSTRB, M_APB_PWDATA } != fnvr_write);
+ assert(M_APB_PADDR != fnvr_addr);
+ end
+
+ always @(posedge gbl_clk)
+ if (M_APB_PSEL && M_PRESETn)
+ begin
+ assert(M_APB_PADDR == S_APB_PADDR);
+ assert(M_APB_PWRITE == S_APB_PWRITE);
+ assert(M_APB_PWDATA == S_APB_PWDATA);
+ assert(M_APB_PWSTRB == S_APB_PWSTRB);
+ end
+
+ always @(posedge gbl_clk)
+ if (fnvr_check && M_APB_PSEL && M_APB_PENABLE && M_APB_PREADY)
+ assume({ M_APB_PSLVERR, M_APB_PRDATA } != fnvr_return);
+
+ always @(posedge gbl_clk)
+ if (fnvr_check && m_ack)
+ assert({ m_pslverr, m_prdata } != fnvr_return);
+
+ always @(posedge gbl_clk)
+ if (fnvr_check && S_APB_PSEL && S_APB_PENABLE && S_APB_PREADY)
+ assert({ S_APB_PSLVERR, S_APB_PRDATA } != fnvr_return);
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ reg [2:0] cvr_reads, cvr_writes;
+
+ initial cvr_writes = 0;
+ always @(posedge S_APB_PCLK)
+ if (!S_PRESETn)
+ cvr_writes <= 0;
+ else if (S_APB_PSEL && S_APB_PENABLE && S_APB_PREADY && S_APB_PWRITE
+ && !cvr_writes[2])
+ cvr_writes <= cvr_writes + 1;
+
+ initial cvr_reads = 0;
+ always @(posedge S_APB_PCLK)
+ if (!S_PRESETn)
+ cvr_reads <= 0;
+ else if (S_APB_PSEL && S_APB_PENABLE && S_APB_PREADY && !S_APB_PWRITE
+ && !cvr_reads[2])
+ cvr_reads <= cvr_reads + 1;
+
+ always @(*)
+ if (S_PRESETn)
+ begin
+ cover(cvr_writes >= 2);
+ cover(cvr_reads >= 2);
+
+ cover(cvr_writes >= 3);
+ cover(cvr_reads >= 3);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_NCLK = 5;
+ reg [F_NCLK-1:0] gbl_sclk, gbl_mclk;
+
+ always @(posedge gbl_clk)
+ gbl_sclk <= { gbl_sclk[F_NCLK-2:0], S_APB_PCLK };
+
+ always @(posedge gbl_clk)
+ gbl_mclk <= { gbl_mclk[F_NCLK-2:0], M_APB_PCLK };
+
+ always @(posedge gbl_clk)
+ if (gbl_sclk == 0)
+ assume(S_APB_PCLK);
+ else if (&gbl_sclk)
+ assume(!S_APB_PCLK);
+
+ always @(posedge gbl_clk)
+ if (gbl_mclk == 0)
+ assume(M_APB_PCLK);
+ else if (&gbl_mclk)
+ assume(!M_APB_PCLK);
+
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axi2axi3.v b/rtl/wb2axip/axi2axi3.v
new file mode 100644
index 0000000..3682949
--- /dev/null
+++ b/rtl/wb2axip/axi2axi3.v
@@ -0,0 +1,897 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi2axi3.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Bridge from an AXI4 slave to an AXI3 master
+//
+// The goal here is to not lose bus resolution, capacity or capability,
+// while bridging from AXI4 to AXI3. The biggest problem with such
+// a bridge is that we'll need to break large requests (AxLEN>15) up
+// into smaller packets. After that, everything should work as normal
+// with only minor modifications for AxCACHE.
+//
+// Opportunity:
+// The cost of this core is very much determined by the number of ID's
+// supported. It should be possible, with little extra cost or effort,
+// to reduce the ID space herein. This should be a topic for future
+// exploration.
+//
+// Status:
+// This core is an unverified first draft. It has past neither formal
+// nor simulation testing. Therefore, it is almost certain to have bugs
+// within it. Use it at your own risk.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+//
+module axi2axi3 #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI4 incoming/slave interface
+ input reg S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input reg [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input reg [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input reg [7:0] S_AXI_AWLEN,
+ input reg [2:0] S_AXI_AWSIZE,
+ input reg [1:0] S_AXI_AWBURST,
+ input reg S_AXI_AWLOCK,
+ input reg [3:0] S_AXI_AWCACHE,
+ input reg [2:0] S_AXI_AWPROT,
+ input reg [3:0] S_AXI_AWQOS,
+ //
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ //
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [7:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire S_AXI_RLAST,
+ output wire [1:0] S_AXI_RRESP,
+ //
+ //
+ // The AXI3 Master (outgoing) interface
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [3:0] M_AXI_AWLEN,
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+ output wire [1:0] M_AXI_AWLOCK,
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ //
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [3:0] M_AXI_ARLEN,
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+ output wire [1:0] M_AXI_ARLOCK,
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ );
+
+ //
+ localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [0:0] OPT_LOWPOWER = 1'b0;
+ localparam LGWFIFO = 4;
+ localparam NID = (1<<C_AXI_ID_WIDTH);
+ parameter LGFIFO = 8;
+
+ genvar ID;
+
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // WRITE SIDE
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write address channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [7:0] r_awlen;
+ reg [3:0] next_wlen;
+ wire awskd_valid;
+ reg awskd_ready;
+ wire [C_AXI_ID_WIDTH-1:0] awskd_id;
+ wire [C_AXI_ADDR_WIDTH-1:0] awskd_addr;
+ wire [7:0] awskd_len;
+ wire [2:0] awskd_size;
+ wire [1:0] awskd_burst;
+ wire awskd_lock;
+ wire [3:0] awskd_cache;
+ wire [2:0] awskd_prot;
+ wire [3:0] awskd_qos;
+ reg axi_awvalid;
+ reg [C_AXI_ID_WIDTH-1:0] axi_awid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [3:0] axi_awlen;
+ reg [2:0] axi_awsize;
+ reg [1:0] axi_awburst;
+ reg [1:0] axi_awlock;
+ reg [3:0] axi_awcache;
+ reg [2:0] axi_awprot;
+ reg [3:0] axi_awqos;
+
+ skidbuffer #(
+ .DW(C_AXI_ADDR_WIDTH + C_AXI_ID_WIDTH + 8 + 3 + 2 + 1+4+3+4),
+ .OPT_OUTREG(1'b0)
+ ) awskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
+ S_AXI_AWSIZE, S_AXI_AWBURST, S_AXI_AWLOCK,
+ S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS }),
+ .o_valid(awskd_valid), .i_ready(awskd_ready),
+ .o_data({ awskd_id, awskd_addr, awskd_len,
+ awskd_size, awskd_burst, awskd_lock,
+ awskd_cache, awskd_prot, awskd_qos })
+ );
+
+ always @(*)
+ begin
+ awskd_ready = 1;
+ if (r_awlen > 0)
+ awskd_ready = 0;
+ if (M_AXI_AWVALID && M_AXI_AWREADY)
+ awskd_ready = 0;
+ if (|wfifo_fill[LGWFIFO:LGWFIFO-1])
+ awskd_ready = 0;
+ end
+
+ initial axi_awvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_awvalid <= 0;
+ else if (awskd_valid || r_awlen > 0)
+ axi_awvalid <= 1;
+ else if (M_AXI_AWREADY)
+ axi_awvalid <= 0;
+
+ initial r_awlen = 0;
+ initial axi_awlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_awlen <= 0;
+ axi_awlen <= 0;
+ end else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ if (r_awlen > 15)
+ begin
+ axi_awlen <= 4'd15;
+ r_awlen <= r_awlen - 8'd16;
+ end else if (r_awlen > 0)
+ begin
+ axi_awlen[3:0] <= r_awlen[3:0] - 1;
+ r_awlen <= 0;
+ end else if (awskd_valid)
+ begin
+ if (awskd_len > 15)
+ begin
+ r_awlen <= awskd_len + 1'b1 - 8'd16;
+ axi_awlen <= 4'd15;
+ end else begin
+ r_awlen <= 0;
+ axi_awlen <= awskd_len[3:0];
+ end
+ end else begin
+ r_awlen <= 0;
+ axi_awlen <= 0;
+ end
+ end
+
+
+ always @(posedge S_AXI_ACLK)
+ if (awskd_valid && awskd_ready)
+ axi_awaddr <= awskd_addr;
+ else if (M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ // Verilator lint_off WIDTH
+ axi_awaddr <= axi_awaddr + ((M_AXI_AWLEN + 1) << M_AXI_AWSIZE);
+ // Verilator lint_on WIDTH
+
+ case(M_AXI_AWSIZE)
+ 3'b000: begin end
+ 3'b001: axi_awaddr[ 0] <= 0;
+ 3'b010: axi_awaddr[1:0] <= 0;
+ 3'b011: axi_awaddr[2:0] <= 0;
+ 3'b100: axi_awaddr[3:0] <= 0;
+ 3'b101: axi_awaddr[4:0] <= 0;
+ 3'b110: axi_awaddr[5:0] <= 0;
+ 3'b111: axi_awaddr[6:0] <= 0;
+ endcase
+ end
+
+
+ initial M_AXI_AWSIZE = ADDRLSB[2:0];
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (awskd_valid && awskd_ready)
+ begin
+ axi_awid <= awskd_id;
+ axi_awsize <= awskd_size;
+ axi_awburst <= awskd_burst;
+ axi_awlock[0]<=awskd_lock;
+ axi_awcache <= awskd_cache;
+ axi_awprot <= awskd_prot;
+ axi_awqos <= awskd_qos;
+ end
+
+ if (C_AXI_DATA_WIDTH < 16)
+ axi_awsize <= 3'b000;
+ else if (C_AXI_DATA_WIDTH < 32)
+ axi_awsize[2:1] <= 2'b00;
+ else if (C_AXI_DATA_WIDTH < 128)
+ axi_awsize[2] <= 1'b0;
+
+ axi_awlock[1] <= 1'b0;
+ end
+
+ assign M_AXI_AWVALID = axi_awvalid;
+ assign M_AXI_AWID = axi_awid;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = axi_awlen;
+ assign M_AXI_AWSIZE = axi_awsize;
+ assign M_AXI_AWBURST = axi_awburst;
+ assign M_AXI_AWLOCK = axi_awlock;
+ assign M_AXI_AWCACHE = axi_awcache;
+ assign M_AXI_AWPROT = axi_awprot;
+ assign M_AXI_AWQOS = axi_awqos;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write data channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire wskd_valid;
+ reg wskd_ready, write_idle;
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ wire wskd_last;
+ reg [4:0] r_wlen;
+ wire [C_AXI_ID_WIDTH-1:0] wfifo_id, raw_wfifo_id;
+ wire [3:0] wfifo_wlen, raw_wfifo_wlen;
+ reg next_wlast, r_wlast;
+ wire raw_wfifo_last;
+ wire wfifo_empty, wfifo_full;
+ wire [LGWFIFO:0] wfifo_fill;
+ reg axi_wvalid;
+ reg [C_AXI_DATA_WIDTH-1:0] axi_wdata;
+ reg [C_AXI_DATA_WIDTH/8-1:0] axi_wstrb;
+ reg [C_AXI_ID_WIDTH-1:0] axi_wid;
+ reg axi_wlast;
+
+ skidbuffer #(
+ .DW(C_AXI_DATA_WIDTH + (C_AXI_DATA_WIDTH/8) + 1),
+ .OPT_OUTREG(1'b0)
+ ) wskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB, S_AXI_WLAST }),
+ .o_valid(wskd_valid), .i_ready(wskd_ready),
+ .o_data({ wskd_data, wskd_strb, wskd_last })
+ );
+
+ always @(*)
+ wskd_ready = !M_AXI_WVALID || M_AXI_WREADY;
+
+ initial axi_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_wvalid <= 0;
+ else if (wskd_valid)
+ axi_wvalid <= 1;
+ else if (M_AXI_WREADY)
+ axi_wvalid <= 0;
+
+ initial write_idle = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ write_idle <= 1;
+ else if (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST && !wskd_valid)
+ write_idle <= 1;
+ else if (wskd_valid && wskd_ready)
+ write_idle <= 0;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ axi_wdata <= wskd_data;
+ axi_wstrb <= wskd_strb;
+
+ if (OPT_LOWPOWER && !wskd_valid)
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ end
+ end
+
+ always @(*)
+ begin
+ if (r_awlen[7:4] != 0)
+ next_wlen = 4'd15;
+ else if (r_awlen[3:0] != 0)
+ next_wlen = r_awlen[3:0];
+ else if (awskd_len[7:4] != 0)
+ next_wlen = 4'd15;
+ else
+ next_wlen = awskd_len[3:0];
+
+ if (r_awlen != 0)
+ next_wlast = (r_awlen[7:4] == 0);
+ else
+ next_wlast = (awskd_len[7:4] == 0);
+ end
+
+ sfifo #(.BW(C_AXI_ID_WIDTH+4+1), .LGFLEN(LGWFIFO))
+ wfifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ (!M_AXI_AWVALID || M_AXI_AWREADY)&&(awskd_valid||(r_awlen > 0))
+ && !write_idle,
+ // The length of the next burst
+ { ((r_awlen != 0) ? M_AXI_AWID : awskd_id),
+ next_wlen, next_wlast },
+ wfifo_full, wfifo_fill,
+ //
+ (!M_AXI_WVALID || M_AXI_WREADY),
+ { raw_wfifo_id, raw_wfifo_wlen, raw_wfifo_last },
+ wfifo_empty);
+
+ assign wfifo_id = (wfifo_empty) ? awskd_id : raw_wfifo_id;
+ assign wfifo_wlen = (wfifo_empty) ? next_wlen : raw_wfifo_wlen;
+
+ initial r_wlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ r_wlen <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (r_wlen > 1)
+ r_wlen <= r_wlen - 1;
+ else if (!wfifo_empty)
+ r_wlen <= raw_wfifo_wlen + 1;
+ else if (awskd_valid && awskd_ready)
+ r_wlen <= next_wlen + 1;
+ end
+
+ initial r_wlast = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ r_wlast <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (r_wlen > 0)
+ r_wlast <= (r_wlen <= 1);
+ else
+ r_wlast <= raw_wfifo_last;
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ begin
+ axi_wid <= 0;
+ axi_wlast <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ axi_wid <= wfifo_id;
+ axi_wlast <= r_wlast;
+
+ if (OPT_LOWPOWER && !wskd_valid)
+ begin
+ axi_wid <= 0;
+ axi_wlast <= 0;
+ end
+ end
+
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WDATA = axi_wdata;
+ assign M_AXI_WSTRB = axi_wstrb;
+ assign M_AXI_WID = axi_wid;
+ assign M_AXI_WLAST = axi_wlast;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write return channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire bskd_valid;
+ reg bskd_ready;
+ wire [C_AXI_ID_WIDTH-1:0] bskd_id;
+ wire [1:0] bskd_resp;
+ reg [1:0] bburst_err [0:NID-1];
+ reg [NID-1:0] wbfifo_valid;
+ reg [NID-1:0] wbfifo_last;
+ reg axi_bvalid;
+ reg [C_AXI_ID_WIDTH-1:0] axi_bid;
+ reg [1:0] axi_bresp;
+
+ skidbuffer #(.DW(C_AXI_ID_WIDTH + 2), .OPT_OUTREG(1'b0)
+ ) bskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(M_AXI_BVALID), .o_ready(M_AXI_BREADY),
+ .i_data({ M_AXI_BID, M_AXI_BRESP }),
+ .o_valid(bskd_valid), .i_ready(bskd_ready),
+ .o_data({ bskd_id, bskd_resp })
+ );
+
+ generate for(ID=0; ID < NID; ID = ID + 1)
+ begin : WRITE_BURST_TRACKING
+ wire wbfifo_empty, wbfifo_full;
+ wire [LGFIFO:0] wbfifo_fill;
+
+ initial wbfifo_valid[ID] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ wbfifo_valid[ID] <= 0;
+ else if (!wbfifo_valid[ID] && !wbfifo_empty)
+ wbfifo_valid[ID] <= 1;
+ else if (bskd_valid && bskd_ready && bskd_id==ID)
+ wbfifo_valid[ID] <= !wbfifo_empty;
+
+ sfifo #(.BW(1), .LGFLEN(LGFIFO))
+ wbfifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ (M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST)
+ && (M_AXI_WID == ID),
+ (r_wlen == 0) ? 1'b1 : 1'b0,
+ wbfifo_full, wbfifo_fill,
+ //
+ (!wbfifo_valid[ID] || (M_AXI_BVALID && M_AXI_BREADY
+ && M_AXI_BID == ID)),
+ wbfifo_last[ID], wbfifo_empty
+ );
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, wbfifo_full, wbfifo_fill };
+ // Verilator lint_on UNUSED
+
+ end endgenerate
+
+ always @(*)
+ bskd_ready = (!S_AXI_BVALID || S_AXI_BREADY);
+
+ initial axi_bvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_bvalid <= 1'b0;
+ else if (bskd_valid && bskd_ready)
+ axi_bvalid <= wbfifo_last[bskd_id];
+ else if (M_AXI_BREADY)
+ axi_bvalid <= 1'b0;
+
+ generate for(ID=0; ID < NID; ID = ID + 1)
+ begin : WRITE_ERR_BY_ID
+
+ initial bburst_err[ID] = 2'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bburst_err[ID] <= 2'b0;
+ else if (S_AXI_BVALID && S_AXI_BREADY && S_AXI_BID == ID
+ && wbfifo_last[ID])
+ bburst_err[ID] <= 2'b0;
+ else if (bskd_valid && bskd_id == ID)
+ bburst_err[ID] <= bburst_err[ID] | bskd_resp;
+
+ end endgenerate
+
+ initial axi_bid = 0;
+ initial axi_bresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_bid <= 0;
+ axi_bresp <= 0;
+ end else if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ axi_bid <= bskd_id;
+ axi_bresp <= bskd_resp | bburst_err[bskd_id];
+
+ if (OPT_LOWPOWER && !bskd_valid)
+ begin
+ axi_bid <= 0;
+ axi_bresp <= 0;
+ end
+ end
+
+ assign S_AXI_BVALID = axi_bvalid;
+ assign S_AXI_BID = axi_bid;
+ assign S_AXI_BRESP = axi_bresp;
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // READ SIDE
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read address channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [7:0] r_arlen;
+ wire arskd_valid;
+ reg arskd_ready;
+ wire [C_AXI_ID_WIDTH-1:0] arskd_id;
+ wire [C_AXI_ADDR_WIDTH-1:0] arskd_addr;
+ wire [7:0] arskd_len;
+ wire [2:0] arskd_size;
+ wire [1:0] arskd_burst;
+ wire arskd_lock;
+ wire [3:0] arskd_cache;
+ wire [2:0] arskd_prot;
+ wire [3:0] arskd_qos;
+ reg axi_arvalid;
+ reg [C_AXI_ID_WIDTH-1:0] axi_arid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+ reg [3:0] axi_arlen;
+ reg [2:0] axi_arsize;
+ reg [1:0] axi_arburst;
+ reg [1:0] axi_arlock;
+ reg [3:0] axi_arcache;
+ reg [2:0] axi_arprot;
+ reg [3:0] axi_arqos;
+
+ skidbuffer #(
+ .DW(C_AXI_ADDR_WIDTH + C_AXI_ID_WIDTH + 8 + 3 + 2 + 1+4+3+4),
+ .OPT_OUTREG(1'b0)
+ ) arskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data({ S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN,
+ S_AXI_ARSIZE, S_AXI_ARBURST, S_AXI_ARLOCK,
+ S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS }),
+ .o_valid(arskd_valid), .i_ready(arskd_ready),
+ .o_data({ arskd_id, arskd_addr, arskd_len,
+ arskd_size, arskd_burst, arskd_lock,
+ arskd_cache, arskd_prot, arskd_qos })
+ );
+
+ always @(*)
+ begin
+ arskd_ready = 1;
+ if (r_arlen > 0)
+ arskd_ready = 0;
+ if (M_AXI_ARVALID && M_AXI_ARREADY)
+ arskd_ready = 0;
+ end
+
+ initial axi_arvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arvalid <= 1'b0;
+ else if (r_arlen > 0)
+ axi_arvalid <= 1'b1;
+ else if (arskd_ready)
+ axi_arvalid <= 1'b1;
+ else if (M_AXI_ARREADY)
+ axi_arvalid <= 1'b0;
+
+ initial r_arlen = 0;
+ initial M_AXI_ARLEN = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_arlen <= 0;
+ axi_arlen <= 0;
+ end else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ if (r_arlen[7:4] != 0)
+ begin
+ axi_arlen <= 4'd15;
+ r_arlen <= r_arlen - 16;
+ end else if (r_arlen[3:0] != 0)
+ begin
+ axi_arlen <= r_arlen[3:0] - 1;
+ r_arlen <= 0;
+ end else if (arskd_valid)
+ begin
+ if (arskd_len[7:4] != 0)
+ begin
+ r_arlen <= arskd_len + 1 - 16;
+ axi_arlen <= 4'd15;
+ end else begin
+ r_arlen <= 0;
+ axi_arlen <= arskd_len[3:0];
+ end
+ end else begin
+ r_arlen <= 0;
+ axi_arlen <= 0;
+ end
+ end
+
+
+ always @(posedge S_AXI_ACLK)
+ if (arskd_valid && arskd_ready)
+ axi_araddr <= arskd_addr;
+ else if (M_AXI_ARVALID && M_AXI_ARREADY)
+ begin
+ // Verilator lint_off WIDTH
+ axi_araddr <= axi_araddr + ((M_AXI_ARLEN + 1) << M_AXI_ARSIZE);
+ // Verilator lint_on WIDTH
+
+ case(M_AXI_AWSIZE)
+ 3'b000: begin end
+ 3'b001: axi_araddr[ 0] <= 0;
+ 3'b010: axi_araddr[1:0] <= 0;
+ 3'b011: axi_araddr[2:0] <= 0;
+ 3'b100: axi_araddr[3:0] <= 0;
+ 3'b101: axi_araddr[4:0] <= 0;
+ 3'b110: axi_araddr[5:0] <= 0;
+ 3'b111: axi_araddr[6:0] <= 0;
+ endcase
+ end
+
+ initial axi_arsize = ADDRLSB[2:0];
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (arskd_valid && arskd_ready)
+ begin
+ axi_arid <= arskd_id;
+ axi_arsize <= arskd_size;
+ axi_arburst <= arskd_burst;
+ axi_arlock[0] <= arskd_lock;
+ axi_arcache <= arskd_cache;
+ axi_arprot <= arskd_prot;
+ axi_arqos <= arskd_qos;
+ end
+
+ // Propagate constants, to help the optimizer out out a touch
+ axi_arlock[1] <= 1'b0;
+
+ if (C_AXI_DATA_WIDTH < 16)
+ axi_arsize <= 3'b000;
+ else if (C_AXI_DATA_WIDTH < 32)
+ axi_arsize[2:1] <= 2'b00;
+ else if (C_AXI_DATA_WIDTH < 128)
+ axi_arsize[2] <= 1'b0;
+ end
+
+ generate for(ID=0; ID < NID; ID = ID + 1)
+ begin : READ_BURST_TRACKING
+ wire rbfifo_empty, rbfifo_full;
+ wire [LGFIFO:0] rbfifo_fill;
+
+ initial rbfifo_valid[ID] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rbfifo_valid[ID] <= 0;
+ else if (!rbfifo_valid[ID] && !rbfifo_empty)
+ rbfifo_valid[ID] <= 1;
+ else if (rskd_valid && rskd_ready && rskd_last && rskd_id==ID)
+ rbfifo_valid[ID] <= !rbfifo_empty;
+
+ sfifo #(.BW(1), .LGFLEN(LGFIFO))
+ rbfifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ (!M_AXI_ARVALID || M_AXI_ARREADY)
+ && (((r_arlen>0)&&(M_AXI_ARID==ID))
+ || (arskd_valid && arskd_id == ID)),
+ ((arskd_valid && arskd_ready && arskd_len <= 15)
+ ||(r_arlen <= 15)) ? 1'b1 : 1'b0,
+ rbfifo_full, rbfifo_fill,
+ //
+ (!rbfifo_valid[ID] || (M_AXI_RVALID && M_AXI_RREADY
+ && M_AXI_RID == ID && M_AXI_RLAST)),
+ rid_last[ID], rbfifo_empty
+ );
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, rbfifo_full, rbfifo_fill };
+ // Verilator lint_on UNUSED
+ end endgenerate
+
+ assign M_AXI_ARVALID= axi_arvalid;
+ assign M_AXI_ARID = axi_arid;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARLEN = axi_arlen;
+ assign M_AXI_ARSIZE = axi_arsize;
+ assign M_AXI_ARBURST= axi_arburst;
+ assign M_AXI_ARLOCK = axi_arlock;
+ assign M_AXI_ARCACHE= axi_arcache;
+ assign M_AXI_ARPROT = axi_arprot;
+ assign M_AXI_ARQOS = axi_arqos;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read data channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire rskd_valid;
+ reg rskd_ready;
+ wire [C_AXI_ID_WIDTH-1:0] rskd_id;
+ wire [C_AXI_DATA_WIDTH-1:0] rskd_data;
+ wire rskd_last;
+ wire [1:0] rskd_resp;
+ reg [1:0] rburst_err [0:NID-1];
+ reg [NID-1:0] rbfifo_valid;
+ reg [NID-1:0] rid_last;
+ reg axi_rvalid;
+ reg [C_AXI_ID_WIDTH-1:0] axi_rid;
+ reg [C_AXI_DATA_WIDTH-1:0] axi_rdata;
+ reg axi_rlast;
+ reg [1:0] axi_rresp;
+
+ skidbuffer #(
+ .DW(C_AXI_DATA_WIDTH + C_AXI_ID_WIDTH + 3),
+ .OPT_OUTREG(1'b0)
+ ) rskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(M_AXI_RVALID), .o_ready(M_AXI_RREADY),
+ .i_data({ M_AXI_RID, M_AXI_RDATA, M_AXI_RLAST,
+ M_AXI_RRESP }),
+ .o_valid(rskd_valid), .i_ready(rskd_ready),
+ .o_data({ rskd_id, rskd_data, rskd_last, rskd_resp })
+ );
+
+ always @(*)
+ rskd_ready = !S_AXI_RVALID || S_AXI_RREADY;
+
+ initial axi_rvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_rvalid <= 1'b0;
+ else if (rskd_valid && rskd_ready)
+ axi_rvalid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axi_rvalid <= 1'b0;
+
+ generate for(ID=0; ID < NID; ID = ID + 1)
+ begin : READ_ERR_BY_ID
+
+ initial rburst_err[ID] = 2'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rburst_err[ID] <= 2'b0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
+ && S_AXI_RID == ID)
+ rburst_err[ID] <= 2'b0;
+ else if (rskd_valid && rskd_id == ID)
+ rburst_err[ID] <= rburst_err[ID] | rskd_resp;
+
+ end endgenerate
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ axi_rid <= rskd_id;
+ axi_rdata <= rskd_data;
+ axi_rresp <= rskd_resp | rburst_err[rskd_id];
+ axi_rlast <= rid_last[rskd_id] && rskd_last;
+ end
+
+ assign S_AXI_RVALID= axi_rvalid;
+ assign S_AXI_RID = axi_rid;
+ assign S_AXI_RDATA = axi_rdata;
+ assign S_AXI_RRESP = axi_rresp;
+ assign S_AXI_RLAST = axi_rlast;
+ // }}}
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, wfifo_fill[LGWFIFO-2:0], wskd_last,
+ wfifo_wlen, wfifo_full };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+//
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+//
+// This design has not been formally verified.
+//
+`endif
+endmodule
diff --git a/rtl/wb2axip/axi2axilite.v b/rtl/wb2axip/axi2axilite.v
new file mode 100644
index 0000000..4b30aec
--- /dev/null
+++ b/rtl/wb2axip/axi2axilite.v
@@ -0,0 +1,1173 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi2axilite.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Convert from AXI to AXI-lite with no performance loss.
+//
+// Performance: The goal of this converter is to convert from AXI to AXI-lite
+// while still maintaining the one-clock per transaction speed
+// of AXI. It currently achieves this goal. The design needs very little
+// configuration to be useful, but you might wish to resize the FIFOs
+// within depending upon the length of your slave's data path. The current
+// FIFO length, LGFIFO=4, is sufficient to maintain full speed. If the
+// slave, however, can maintain full speed but requires a longer
+// processing cycle, then you may need longer FIFOs.
+//
+// The AXI specification does require an additional 2 clocks per
+// transaction when using this core, so your latency will go up.
+//
+// Related: There's a related axidouble.v core in the same repository as
+// well. That can be used to convert the AXI protocol to something
+// simpler (even simpler than AXI-lite), but it can also do so for multiple
+// downstream slaves at the same time.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axi2axilite #(
+ // {{{
+ parameter integer C_AXI_ID_WIDTH = 2,
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 6,
+ parameter [0:0] OPT_WRITES = 1,
+ parameter [0:0] OPT_READS = 1,
+ parameter [0:0] OPT_LOWPOWER = 0,
+ // Log (based two) of the maximum number of outstanding AXI
+ // (not AXI-lite) transactions. If you multiply 2^LGFIFO * 256,
+ // you'll get the maximum number of outstanding AXI-lite
+ // transactions
+ parameter LGFIFO = 4
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI4 slave interface
+ // {{{
+ // Write address channel
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [7:0] S_AXI_AWLEN,
+ input wire [2:0] S_AXI_AWSIZE,
+ input wire [1:0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [3:0] S_AXI_AWCACHE,
+ input wire [2:0] S_AXI_AWPROT,
+ input wire [3:0] S_AXI_AWQOS,
+ // }}}
+ // Write data channel
+ // {{{
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [(C_AXI_DATA_WIDTH/8)-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ // }}}
+ // Write return channel
+ // {{{
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ // }}}
+ // Read address channel
+ // {{{
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [7:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+ // }}}
+ // Read data channel
+ // {{{
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ output wire S_AXI_RLAST,
+ // }}}
+ // }}}
+ // AXI-lite master interface
+ // {{{
+ // AXI-lite Write interface
+ // {{{
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [2 : 0] M_AXI_AWPROT,
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [(C_AXI_DATA_WIDTH/8)-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ input wire [1 : 0] M_AXI_BRESP,
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ // }}}
+ // AXI-lite read interface
+ // {{{
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,
+ input wire [1 : 0] M_AXI_RRESP
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ // Local parameters, register, and net declarations
+ // {{{
+ localparam [1:0] SLVERR = 2'b10;
+ // localparam [1:0] OKAY = 2'b00,
+ // EXOKAY = 2'b01,
+ // DECERR = 2'b10;
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam IW = C_AXI_ID_WIDTH;
+ // }}}
+ // Register declarations
+ // {{{
+ //
+ // Write registers
+ reg m_axi_awvalid, s_axi_wready;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [7:0] axi_awlen, axi_blen;
+ reg [1:0] axi_awburst;
+ reg [2:0] axi_awsize;
+ wire [C_AXI_ADDR_WIDTH-1:0] next_write_addr;
+ wire [4:0] wfifo_count;
+ wire wfifo_full;
+ wire wfifo_empty;
+ wire [7:0] wfifo_bcount;
+ wire [IW-1:0] wfifo_bid;
+ reg [8:0] bcounts;
+ reg [C_AXI_ID_WIDTH-1:0] axi_bid, bid;
+ reg [1:0] axi_bresp;
+ reg s_axi_bvalid;
+ wire read_from_wrfifo;
+ //
+ // Read register
+ reg m_axi_arvalid;
+ wire [4:0] rfifo_count;
+ wire rfifo_full;
+ wire rfifo_empty;
+ wire [7:0] rfifo_rcount;
+ reg s_axi_rvalid;
+ reg [1:0] s_axi_rresp;
+ reg [8:0] rcounts;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+ reg [7:0] axi_arlen, axi_rlen;
+ reg [1:0] axi_arburst;
+ reg [2:0] axi_arsize;
+ wire [C_AXI_ADDR_WIDTH-1:0] next_read_addr;
+ reg [C_AXI_ID_WIDTH-1:0] s_axi_rid;
+ wire [C_AXI_ID_WIDTH-1:0] rfifo_rid;
+ reg [C_AXI_DATA_WIDTH-1:0] s_axi_rdata;
+ reg s_axi_rlast;
+ reg [IW-1:0] rid;
+ wire read_from_rdfifo;
+
+ //
+ // S_AXI_AW* skid buffer
+ wire skids_awvalid, skids_awready;
+ wire [IW-1:0] skids_awid;
+ wire [AW-1:0] skids_awaddr;
+ wire [7:0] skids_awlen;
+ wire [2:0] skids_awsize;
+ wire [1:0] skids_awburst;
+ //
+ // S_AXI_W* skid buffer
+ wire skids_wvalid, skids_wready, skids_wlast;
+ wire [DW-1:0] skids_wdata;
+ wire [DW/8-1:0] skids_wstrb;
+ //
+ // S_AXI_B* skid buffer isn't needed
+ //
+ // M_AXI_AW* skid buffer isn't needed
+ //
+ // M_AXI_W* skid buffer
+ wire skidm_wvalid, skidm_wready;
+ wire [DW-1:0] skidm_wdata;
+ wire [DW/8-1:0] skidm_wstrb;
+ //
+ // M_AXI_B* skid buffer
+ wire skidm_bvalid, skidm_bready;
+ wire [1:0] skidm_bresp;
+ //
+ //
+ //
+ // S_AXI_AR* skid buffer
+ wire skids_arvalid, skids_arready;
+ wire [IW-1:0] skids_arid;
+ wire [AW-1:0] skids_araddr;
+ wire [7:0] skids_arlen;
+ wire [2:0] skids_arsize;
+ wire [1:0] skids_arburst;
+ //
+ // S_AXI_R* skid buffer isn't needed
+ //
+ // M_AXI_AR* skid buffer isn't needed
+ // M_AXI_R* skid buffer
+ wire skidm_rvalid, skidm_rready;
+ wire [DW-1:0] skidm_rdata;
+ wire [1:0] skidm_rresp;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_WRITES)
+ begin : IMPLEMENT_WRITES
+ // {{{
+ // The write address channel's skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2), .OPT_LOWPOWER(0), .OPT_OUTREG(0)
+ // }}}
+ ) awskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_AWVALID, S_AXI_AWREADY,
+ { S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN, S_AXI_AWSIZE,
+ S_AXI_AWBURST },
+ skids_awvalid, skids_awready,
+ { skids_awid, skids_awaddr, skids_awlen, skids_awsize,
+ skids_awburst }
+ // }}}
+ );
+ // }}}
+ //
+ // The write data channel's skid buffer (S_AXI_W*)
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(DW+DW/8+1), .OPT_LOWPOWER(0), .OPT_OUTREG(0)
+ // }}}
+ ) wskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_WVALID, S_AXI_WREADY,
+ { S_AXI_WDATA, S_AXI_WSTRB, S_AXI_WLAST },
+ skids_wvalid, skids_wready,
+ { skids_wdata, skids_wstrb, skids_wlast }
+ // }}}
+ );
+ // }}}
+ //
+ // The downstream AXI-lite write data (M_AXI_W*) skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(DW+DW/8), .OPT_LOWPOWER(0), .OPT_OUTREG(1)
+ // }}}
+ ) mwskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ skidm_wvalid, skidm_wready, { skidm_wdata, skidm_wstrb },
+ M_AXI_WVALID,M_AXI_WREADY,{ M_AXI_WDATA, M_AXI_WSTRB }
+ // }}}
+ );
+ // }}}
+ //
+ // The downstream AXI-lite response (M_AXI_B*) skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(2), .OPT_LOWPOWER(0), .OPT_OUTREG(0)
+ // }}}
+ ) bskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ M_AXI_BVALID, M_AXI_BREADY, { M_AXI_BRESP },
+ skidm_bvalid, skidm_bready, { skidm_bresp }
+ // }}}
+ );
+ // }}}
+
+ // m_axi_awvalid
+ // {{{
+ initial m_axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_awvalid <= 0;
+ else if (skids_awvalid & skids_awready)
+ m_axi_awvalid <= 1;
+ else if (M_AXI_AWREADY && axi_awlen == 0)
+ m_axi_awvalid <= 0;
+
+ assign M_AXI_AWVALID = m_axi_awvalid;
+ // }}}
+
+ // skids_awready
+ // {{{
+ assign skids_awready = (!M_AXI_AWVALID
+ || ((axi_awlen == 0)&&M_AXI_AWREADY))
+ && !wfifo_full
+ &&(!s_axi_wready || (skids_wvalid && skids_wlast && skids_wready));
+ // }}}
+
+ // Address processing
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (skids_awvalid && skids_awready)
+ begin
+ axi_awaddr <= skids_awaddr;
+ axi_blen <= skids_awlen;
+ axi_awburst<= skids_awburst;
+ axi_awsize <= skids_awsize;
+ end else if (M_AXI_AWVALID && M_AXI_AWREADY)
+ axi_awaddr <= next_write_addr;
+ // }}}
+
+ // axi_awlen
+ // {{{
+ initial axi_awlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_awlen <= 0;
+ else if (skids_awvalid && skids_awready)
+ axi_awlen <= skids_awlen;
+ else if (M_AXI_AWVALID && M_AXI_AWREADY && axi_awlen > 0)
+ axi_awlen <= axi_awlen - 1;
+ // }}}
+
+ // axi_addr
+ // {{{
+ axi_addr #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH)
+ // }}}
+ ) calcwraddr(
+ // {{{
+ axi_awaddr, axi_awsize, axi_awburst,
+ axi_blen, next_write_addr
+ // }}}
+ );
+ // }}}
+
+ // s_axi_wready
+ // {{{
+ // We really don't need to do anything special to the write
+ // channel.
+ initial s_axi_wready = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_wready <= 0;
+ else if (skids_awvalid && skids_awready)
+ s_axi_wready <= 1;
+ else if (skids_wvalid && skids_wready && skids_wlast)
+ s_axi_wready <= 0;
+ // }}}
+
+ // skidm*, and read_from_wrfifo
+ // {{{
+ assign skidm_wdata = skids_wdata;
+ assign skidm_wstrb = skids_wstrb;
+ assign skidm_wvalid = skids_wvalid && s_axi_wready;
+ assign skids_wready = s_axi_wready && skidm_wready;
+
+ assign read_from_wrfifo = (bcounts <= 1)&&(!wfifo_empty)
+ &&(skidm_bvalid && skidm_bready);
+ // }}}
+
+ // BFIFO
+ // {{{
+ sfifo #(
+ .BW(C_AXI_ID_WIDTH+8), .LGFLEN(LGFIFO)
+ ) bidlnfifo(
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ skids_awvalid && skids_awready,
+ { skids_awid, skids_awlen },
+ wfifo_full, wfifo_count,
+ read_from_wrfifo,
+ { wfifo_bid, wfifo_bcount }, wfifo_empty);
+ // }}}
+
+ // bcounts
+ // {{{
+ // Return counts
+ initial bcounts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bcounts <= 0;
+ else if (read_from_wrfifo)
+ begin
+ bcounts <= wfifo_bcount + bcounts;
+ end else if (skidm_bvalid && skidm_bready)
+ bcounts <= bcounts - 1;
+ // }}}
+
+ // bid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (read_from_wrfifo)
+ bid <= wfifo_bid;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ axi_bid <= (read_from_wrfifo && bcounts==0) ? wfifo_bid : bid;
+ // }}}
+
+ // s_axi_bvalid
+ // {{{
+ initial s_axi_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_bvalid <= 0;
+ else if (skidm_bvalid && skidm_bready)
+ s_axi_bvalid <= (bcounts == 1)
+ ||((bcounts == 0) && (!wfifo_empty) && (wfifo_bcount == 0));
+ else if (S_AXI_BREADY)
+ s_axi_bvalid <= 0;
+ // }}}
+
+ // axi_bresp
+ // {{{
+ initial axi_bresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_bresp <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ begin
+ if (skidm_bvalid && skidm_bready)
+ axi_bresp <= skidm_bresp;
+ else
+ axi_bresp <= 0;
+ end else if (skidm_bvalid && skidm_bready)
+ begin
+ // Let SLVERR take priority over DECERR
+ casez({ S_AXI_BRESP, skidm_bresp })
+ 4'b??0?: axi_bresp <= S_AXI_BRESP;
+ 4'b0?1?: axi_bresp <= skidm_bresp;
+ 4'b1?10: axi_bresp <= SLVERR;
+ 4'b1011: axi_bresp <= SLVERR;
+ 4'b1111: axi_bresp <= skidm_bresp;
+ endcase
+ end
+ // /}}}
+
+ // M_AXI_AW*
+ // {{{
+ assign M_AXI_AWVALID= m_axi_awvalid;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWPROT = 0;
+ // }}}
+
+ // skidm_bready, S_AXI_B*
+ // {{{
+ assign skidm_bready = ((bcounts > 0)||(!wfifo_empty))&&(!S_AXI_BVALID | S_AXI_BREADY);
+ assign S_AXI_BID = axi_bid;
+ assign S_AXI_BRESP = axi_bresp;
+ assign S_AXI_BVALID = s_axi_bvalid;
+ // }}}
+ // }}}
+ end else begin : NO_WRITE_SUPPORT
+ // {{{
+ assign S_AXI_AWREADY = 0;
+ assign S_AXI_WREADY = 0;
+ assign S_AXI_BID = 0;
+ assign S_AXI_BRESP = 2'b11;
+ assign S_AXI_BVALID = 0;
+ assign S_AXI_BID = 0;
+
+ //
+ assign M_AXI_AWVALID = 0;
+ assign M_AXI_AWADDR = 0;
+ assign M_AXI_AWPROT = 0;
+ //
+ assign M_AXI_WVALID = 0;
+ assign M_AXI_WDATA = 0;
+ assign M_AXI_WSTRB = 0;
+ //
+ assign M_AXI_BREADY = 0;
+
+ //
+ // S_AXI_AW* skid buffer
+ assign skids_awvalid = 0;
+ assign skids_awready = 0;
+ assign skids_awid = 0;
+ assign skids_awaddr = 0;
+ assign skids_awlen = 0;
+ assign skids_awsize = 0;
+ assign skids_awburst = 0;
+ //
+ // S_AXI_W* skid buffer
+ assign skids_wvalid = S_AXI_WVALID;
+ assign skids_wready = S_AXI_WREADY;
+ assign skids_wdata = S_AXI_WDATA;
+ assign skids_wstrb = S_AXI_WSTRB;
+ assign skids_wlast = S_AXI_WLAST;
+ //
+ // S_AXI_B* skid buffer isn't needed
+ //
+ // M_AXI_AW* skid buffer isn't needed
+ //
+ // M_AXI_W* skid buffer
+ assign skidm_wvalid = M_AXI_WVALID;
+ assign skidm_wready = M_AXI_WREADY;
+ assign skidm_wdata = M_AXI_WDATA;
+ assign skidm_wstrb = M_AXI_WSTRB;
+ //
+ // M_AXI_B* skid buffer
+ assign skidm_bvalid = M_AXI_BVALID;
+ assign skidm_bready = M_AXI_BREADY;
+ assign skidm_bresp = M_AXI_BRESP;
+ //
+ //
+ always @(*)
+ begin
+ s_axi_wready = 0;
+
+ axi_awlen = 0;
+ bcounts = 0;
+ bid = 0;
+ axi_bresp = 0;
+ axi_bid = 0;
+
+ end
+
+ assign wfifo_full = 0;
+ assign wfifo_empty = 1;
+ assign wfifo_count = 0;
+ assign read_from_wrfifo = 0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_write_signals;
+ assign unused_write_signals = &{ 1'b0, M_AXI_AWREADY,
+ M_AXI_WREADY, S_AXI_BREADY };
+ // Verilator lint_on UNUSED
+ // }}}
+
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_READS)
+ begin : IMPLEMENT_READS
+ // {{{
+ // S_AXI_AR* skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2), .OPT_LOWPOWER(0), .OPT_OUTREG(0)
+ // }}}
+ ) arskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_ARVALID, S_AXI_ARREADY,
+ { S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN, S_AXI_ARSIZE,
+ S_AXI_ARBURST },
+ skids_arvalid, skids_arready,
+ { skids_arid, skids_araddr, skids_arlen, skids_arsize,
+ skids_arburst }
+ // }}}
+ );
+ // }}}
+ // M_AXI_R* skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(DW+2), .OPT_LOWPOWER(0), .OPT_OUTREG(0)
+ // }}}
+ ) rskid(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ M_AXI_RVALID, M_AXI_RREADY,{ M_AXI_RDATA, M_AXI_RRESP },
+ skidm_rvalid,skidm_rready,{ skidm_rdata, skidm_rresp }
+ // }}}
+ );
+ // }}}
+ // m_axi_arvalid
+ // {{{
+ initial m_axi_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_arvalid <= 0;
+ else if (skids_arvalid && skids_arready)
+ m_axi_arvalid <= 1;
+ else if (M_AXI_ARREADY && axi_arlen == 0)
+ m_axi_arvalid <= 0;
+ // }}}
+
+ // Read address processing
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (skids_arvalid && skids_arready)
+ begin
+ axi_araddr <= skids_araddr;
+ axi_arburst <= skids_arburst;
+ axi_arsize <= skids_arsize;
+ axi_rlen <= skids_arlen;
+ end else if (M_AXI_ARREADY)
+ begin
+ axi_araddr <= next_read_addr;
+ if (OPT_LOWPOWER && axi_arlen == 0)
+ axi_araddr <= 0;
+ end
+
+ axi_addr #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH)
+ // }}}
+ ) calcrdaddr(
+ // {{{
+ axi_araddr, axi_arsize, axi_arburst,
+ axi_rlen, next_read_addr
+ // }}}
+ );
+ // }}}
+
+ // axi_arlen, Read length processing
+ // {{{
+ initial axi_arlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arlen <= 0;
+ else if (skids_arvalid && skids_arready)
+ axi_arlen <= skids_arlen;
+ else if (M_AXI_ARVALID && M_AXI_ARREADY && axi_arlen > 0)
+ axi_arlen <= axi_arlen - 1;
+ // }}}
+
+ assign skids_arready = (!M_AXI_ARVALID ||
+ ((axi_arlen == 0) && M_AXI_ARREADY))
+ && !rfifo_full;
+
+ assign read_from_rdfifo = skidm_rvalid && skidm_rready
+ && (rcounts <= 1) && !rfifo_empty;
+
+ // Read ID FIFO
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(C_AXI_ID_WIDTH+8), .LGFLEN(LGFIFO)
+ // }}}
+ ) ridlnfifo(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN,
+ skids_arvalid && skids_arready,
+ { skids_arid, skids_arlen },
+ rfifo_full, rfifo_count,
+ read_from_rdfifo,
+ { rfifo_rid, rfifo_rcount }, rfifo_empty
+ // }}}
+ );
+ // }}}
+
+ assign skidm_rready = (!S_AXI_RVALID || S_AXI_RREADY);
+
+ // s_axi_rvalid
+ // {{{
+ initial s_axi_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_rvalid <= 0;
+ else if (skidm_rvalid && skidm_rready)
+ s_axi_rvalid <= 1;
+ else if (S_AXI_RREADY)
+ s_axi_rvalid <= 0;
+ // }}}
+
+ // s_axi_rresp, s_axi_rdata
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (skidm_rvalid && skidm_rready)
+ begin
+ s_axi_rresp <= skidm_rresp;
+ s_axi_rdata <= skidm_rdata;
+ end else if (S_AXI_RREADY)
+ begin
+ s_axi_rresp <= 0;
+ s_axi_rdata <= 0;
+ end
+ // }}}
+
+ // rcounts, Return counts
+ // {{{
+ initial rcounts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rcounts <= 0;
+ else if (read_from_rdfifo)
+ rcounts <= rfifo_rcount + rcounts;
+ else if (skidm_rvalid && skidm_rready)
+ rcounts <= rcounts - 1;
+ // }}}
+
+ // rid
+ // {{{
+ initial rid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (read_from_rdfifo)
+ rid <= rfifo_rid;
+ // }}}
+
+ // s_axi_rlast
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ // if (rcounts == 1) s_axi_rlast <= 1; else
+ if (read_from_rdfifo)
+ s_axi_rlast <= (rfifo_rcount == 0);
+ else
+ s_axi_rlast <= 0;
+
+ if (rcounts == 1)
+ s_axi_rlast <= 1;
+ end
+ // }}}
+
+ // s_axi_rid
+ // {{{
+ initial s_axi_rid = 0;
+ always @(posedge S_AXI_ACLK)
+ if ((S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ ||(!S_AXI_RVALID && rcounts == 0))
+ s_axi_rid <= (read_from_rdfifo)&&(rcounts == 0)?rfifo_rid : rid;
+ // }}}
+
+ // M_AXI_AR*
+ // {{{
+ assign M_AXI_ARVALID= m_axi_arvalid;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARPROT = 0;
+ // }}}
+ // S_AXI_R*
+ // {{{
+ assign S_AXI_RVALID = s_axi_rvalid;
+ assign S_AXI_RDATA = s_axi_rdata;
+ assign S_AXI_RRESP = s_axi_rresp;
+ assign S_AXI_RLAST = s_axi_rlast;
+ assign S_AXI_RID = s_axi_rid;
+ // }}}
+ // }}}
+ end else begin : NO_READ_SUPPORT // if (!OPT_READS)
+ // {{{
+ assign M_AXI_ARVALID= 0;
+ assign M_AXI_ARADDR = 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_RREADY = 0;
+ //
+ assign S_AXI_ARREADY= 0;
+ assign S_AXI_RVALID = 0;
+ assign S_AXI_RDATA = 0;
+ assign S_AXI_RRESP = 0;
+ assign S_AXI_RLAST = 0;
+ assign S_AXI_RID = 0;
+
+ //
+ assign skids_arvalid = S_AXI_ARVALID;
+ assign skids_arready = S_AXI_ARREADY;
+ assign skids_arid = S_AXI_ARID;
+ assign skids_araddr = S_AXI_ARADDR;
+ assign skids_arlen = S_AXI_ARLEN;
+ assign skids_arsize = S_AXI_ARSIZE;
+ assign skids_arburst = S_AXI_ARBURST;
+ //
+ assign skidm_rvalid = M_AXI_RVALID;
+ assign skidm_rready = M_AXI_RREADY;
+ assign skidm_rdata = M_AXI_RDATA;
+ assign skidm_rresp = M_AXI_RRESP;
+ //
+ //
+ always @(*)
+ begin
+ axi_arlen = 0;
+
+ rcounts = 0;
+ rid = 0;
+
+ end
+ assign rfifo_empty = 1;
+ assign rfifo_full = 0;
+ assign rfifo_count = 0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_read_signals;
+ assign unused_read_signals = &{ 1'b0, M_AXI_ARREADY,
+ S_AXI_RREADY };
+ // Verilator lint_on UNUSED
+ // }}}
+
+ // }}}
+ end endgenerate
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire [35-1:0] unused;
+ assign unused = {
+ S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS,
+ skids_wlast, wfifo_count,
+ S_AXI_ARLOCK, S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS,
+ rfifo_count };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+// The following are a subset of the formal properties used to verify this core
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGFIFO+1+8;
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI channel properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxi_slave #(.C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(DW),
+ .C_AXI_ADDR_WIDTH(AW),
+ //
+ )
+ faxi(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address
+ .i_axi_awready(skids_awready),
+ .i_axi_awid( skids_awid),
+ .i_axi_awaddr( skids_awaddr),
+ .i_axi_awlen( skids_awlen),
+ .i_axi_awsize( skids_awsize),
+ .i_axi_awburst(skids_awburst),
+ .i_axi_awlock( 0),
+ .i_axi_awcache(0),
+ .i_axi_awprot( 0),
+ .i_axi_awqos( 0),
+ .i_axi_awvalid(skids_awvalid),
+ // Write data
+ .i_axi_wready( skids_wready),
+ .i_axi_wdata( skids_wdata),
+ .i_axi_wstrb( skids_wstrb),
+ .i_axi_wlast( skids_wlast),
+ .i_axi_wvalid( skids_wvalid),
+ // Write return response
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ .i_axi_bvalid( S_AXI_BVALID),
+ .i_axi_bready( S_AXI_BREADY),
+ // Read address
+ .i_axi_arready(skids_arready),
+ .i_axi_arid( skids_arid),
+ .i_axi_araddr( skids_araddr),
+ .i_axi_arlen( skids_arlen),
+ .i_axi_arsize( skids_arsize),
+ .i_axi_arburst(skids_arburst),
+ .i_axi_arlock( 0),
+ .i_axi_arcache(0),
+ .i_axi_arprot( 0),
+ .i_axi_arqos( 0),
+ .i_axi_arvalid(skids_arvalid),
+ // Read response
+ .i_axi_rid( S_AXI_RID),
+ .i_axi_rresp( S_AXI_RRESP),
+ .i_axi_rvalid( S_AXI_RVALID),
+ .i_axi_rdata( S_AXI_RDATA),
+ .i_axi_rlast( S_AXI_RLAST),
+ .i_axi_rready( S_AXI_RREADY),
+ //
+ // Formal property data
+ .f_axi_awr_nbursts( faxi_awr_nbursts),
+ .f_axi_wr_pending( faxi_wr_pending),
+ .f_axi_rd_nbursts( faxi_rd_nbursts),
+ .f_axi_rd_outstanding(faxi_rd_outstanding),
+ //
+ // ...
+ );
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxil_master #(.C_AXI_DATA_WIDTH(DW), .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_NO_RESET(1),
+ .F_AXI_MAXWAIT(5),
+ .F_AXI_MAXDELAY(4),
+ .F_AXI_MAXRSTALL(0),
+ .F_OPT_WRITE_ONLY(OPT_WRITES && !OPT_READS),
+ .F_OPT_READ_ONLY(!OPT_WRITES && OPT_READS),
+ .F_LGDEPTH(F_AXIL_LGDEPTH))
+ faxil(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address channel
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awprot( M_AXI_AWPROT),
+ // Write data
+ .i_axi_wready( skidm_wready),
+ .i_axi_wdata( skidm_wdata),
+ .i_axi_wstrb( skidm_wstrb),
+ .i_axi_wvalid( skidm_wvalid),
+ // Write response
+ .i_axi_bresp( skidm_bresp),
+ .i_axi_bvalid( skidm_bvalid),
+ .i_axi_bready( skidm_bready),
+ // Read address
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arprot( M_AXI_ARPROT),
+ // Read data return
+ .i_axi_rvalid( skidm_rvalid),
+ .i_axi_rready( skidm_rready),
+ .i_axi_rdata( skidm_rdata),
+ .i_axi_rresp( skidm_rresp),
+ //
+ // Formal check variables
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding));
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assume that the two write channels stay within an appropriate
+ // distance of each other. This is to make certain that the property
+ // file features are not violated, although not necessary true for
+ // actual operation
+ //
+ always @(*)
+ assert(s_axi_wready == (OPT_WRITES && faxi_wr_pending > 0));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // These are extra properties necessary to pass write induction
+ //
+
+ always @(*)
+ if ((bcounts == 0)&&(!read_from_wrfifo))
+ assert(!skidm_bvalid || !skidm_bready);
+
+ always @(*)
+ if (axi_awlen > 0)
+ begin
+ assert(m_axi_awvalid);
+ if (axi_awlen > 1)
+ begin
+ assert(!skids_awready);
+ end else if (wfifo_full)
+ begin
+ assert(!skids_awready);
+ end else if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ assert(!skids_awready);
+ end
+
+
+ always @(*)
+ assert(axi_bresp != EXOKAY);
+
+ reg [F_LGDEPTH-1:0] f_wfifo_bursts, f_wfifo_bursts_minus_one,
+ f_wfifo_within,
+ f_wfiid_bursts, f_wfiid_bursts_minus_one;
+ reg [IW-1:0] f_awid;
+ always @(posedge S_AXI_ACLK)
+ if (skids_awvalid && skids_awready)
+ f_awid = skids_awid;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (!S_AXI_RVALID && rcounts > 0)
+ assert(rid == S_AXI_RID);
+
+ always @(*)
+ if (S_AXI_RVALID && !S_AXI_RLAST)
+ assert(rid == S_AXI_RID);
+
+ always @(*)
+ if ((rcounts == 0)&&(!read_from_rdfifo))
+ assert(!skidm_rvalid || !skidm_rready);
+
+ always @(*)
+ if (axi_arlen > 0)
+ begin
+ assert(m_axi_arvalid);
+ assert(!skids_arready);
+ end
+
+ //
+ // ...
+ //
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Select only write or only read operation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (!OPT_WRITES)
+ begin
+ always @(*)
+ begin
+ assume(!skids_awvalid);
+ assume(!skids_wvalid);
+ assert(M_AXI_AWVALID == 0);
+ assert(faxil_awr_outstanding == 0);
+ assert(faxil_wr_outstanding == 0);
+ assert(!skidm_bvalid);
+ assert(!S_AXI_BVALID);
+ end
+ end endgenerate
+
+ generate if (!OPT_READS)
+ begin
+
+ always @(*)
+ begin
+ assume(!S_AXI_ARVALID);
+ assert(M_AXI_ARVALID == 0);
+ assert(faxil_rd_outstanding == 0);
+ end
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover statements, to show performance
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_WRITES)
+ begin
+ // {{{
+ reg [3:0] cvr_write_count, cvr_write_count_simple;
+
+ initial cvr_write_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_write_count_simple <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN == 0)
+ cvr_write_count_simple <= cvr_write_count_simple + 1;
+
+ initial cvr_write_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_write_count <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN > 2)
+ cvr_write_count <= cvr_write_count + 1;
+
+ always @(*)
+ cover(cvr_write_count_simple > 6 && /* ... */ !S_AXI_BVALID);
+ always @(*)
+ cover(cvr_write_count > 2 && /* ... */ !S_AXI_BVALID);
+ // }}}
+ end endgenerate
+
+ generate if (OPT_READS)
+ begin
+ // {{{
+ reg [3:0] cvr_read_count, cvr_read_count_simple;
+
+ initial cvr_read_count_simple = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_count_simple <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 0)
+ cvr_read_count_simple <= cvr_read_count_simple + 1;
+
+ initial cvr_read_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_count <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 2)
+ cvr_read_count <= cvr_read_count + 1;
+
+ always @(*)
+ cover(cvr_read_count_simple > 6 && /* ... */ !S_AXI_RVALID);
+ always @(*)
+ cover(cvr_read_count > 2 && /* ... */ !S_AXI_RVALID);
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ...
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // }}}
+`undef BMC_ASSERT
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axi2axilsub.v b/rtl/wb2axip/axi2axilsub.v
new file mode 100644
index 0000000..694dc58
--- /dev/null
+++ b/rtl/wb2axip/axi2axilsub.v
@@ -0,0 +1,2157 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi2axilsub.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Convert from AXI to AXI-lite with no performance loss, and to
+// a potentially smaller bus width.
+//
+// Performance: The goal of this converter is to convert from AXI to an AXI-lite
+// bus of a smaller width, while still maintaining the one-clock
+// per transaction speed of AXI. It currently achieves this goal. The
+// design needs very little configuration to be useful, but you might
+// wish to resize the FIFOs within depending upon the length of your
+// slave's data path. The current FIFO length, LGFIFO=4, is sufficient to
+// maintain full speed. If the slave, however, can maintain full speed but
+// requires a longer processing cycle, then you may need longer FIFOs.
+//
+// The AXI specification does require an additional 2 clocks per
+// transaction when using this core, so your latency will go up.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This digital logic component is the proprietary property of Gisselquist
+// Technology, LLC. It may only be distributed and/or re-distributed by the
+// express permission of Gisselquist Technology.
+//
+// Permission has been granted to the Patreon sponsors of the ZipCPU blog
+// to use this logic component as they see fit, but not to redistribute it
+// beyond their individual personal or commercial use.
+//
+// This component is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
+// or FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Please feel free to contact me should you have any questions, or even if you
+// just want to ask about what you find within here.
+//
+// Yours,
+//
+// Dan Gisselquist
+// Owner
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+//
+`ifdef FORMAL
+`ifdef BMC
+`define BMC_ASSERT assert
+`else
+`define BMC_ASSERT assume
+`endif
+`endif
+//
+// }}}
+module axi2axilsub #(
+ // {{{
+ parameter integer C_AXI_ID_WIDTH = 2,
+ parameter integer C_S_AXI_DATA_WIDTH = 64,
+ parameter integer C_M_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 6,
+ parameter [0:0] OPT_LOWPOWER = 1,
+ parameter [0:0] OPT_WRITES = 1,
+ parameter [0:0] OPT_READS = 1,
+ parameter SLVSZ = $clog2(C_S_AXI_DATA_WIDTH/8),
+ parameter MSTSZ = $clog2(C_M_AXI_DATA_WIDTH/8),
+ // Log (based two) of the maximum number of outstanding AXI
+ // (not AXI-lite) transactions. If you multiply 2^LGFIFO * 256,
+ // you'll get the maximum number of outstanding AXI-lite
+ // transactions
+ parameter LGFIFO = 4
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI4 slave interface
+ // {{{
+ // Write address channel
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [7:0] S_AXI_AWLEN,
+ input wire [2:0] S_AXI_AWSIZE,
+ input wire [1:0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [3:0] S_AXI_AWCACHE,
+ input wire [2:0] S_AXI_AWPROT,
+ input wire [3:0] S_AXI_AWQOS,
+ // }}}
+ // Write data channel
+ // {{{
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [(C_S_AXI_DATA_WIDTH/8)-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ // }}}
+ // Write return channel
+ // {{{
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ // }}}
+ // Read address channel
+ // {{{
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [7:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+ // }}}
+ // Read data channel
+ // {{{
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ output wire S_AXI_RLAST,
+ // }}}
+ // }}}
+ // AXI-lite master interface
+ // {{{
+ // AXI-lite Write interface
+ // {{{
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [2 : 0] M_AXI_AWPROT,
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_M_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [(C_M_AXI_DATA_WIDTH/8)-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ input wire [1 : 0] M_AXI_BRESP,
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ // }}}
+ // AXI-lite read interface
+ // {{{
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,
+ input wire [1 : 0] M_AXI_RRESP
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ // Local parameters, register, and net declarations
+ // {{{
+ // Verilator lint_off UNUSED
+ localparam [1:0] OKAY = 2'b00,
+ EXOKAY = 2'b01,
+ SLVERR = 2'b10;
+ // localparam [1:0] DECERR = 2'b10;
+
+ // Verilator lint_on UNUSED
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam SLVDW = C_S_AXI_DATA_WIDTH;
+ localparam MSTDW = C_M_AXI_DATA_WIDTH;
+ localparam IW = C_AXI_ID_WIDTH;
+ // localparam LSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ // }}}
+ // Register declarations
+ // {{{
+ //
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_WRITES && SLVDW == MSTDW)
+ begin : IMPLEMENT_AXI2AXILITE_WRITES
+ // {{{
+
+ // (Unused) signal declarations
+ // {{{
+ // Verilator lint_off UNUSED
+ wire ign_arready, ign_rvalid,
+ ign_rlast,
+ ign_arvalid, ign_rready;
+ wire [IW-1:0] ign_rid;
+ wire [SLVDW-1:0] ign_rdata;
+ wire [1:0] ign_rresp;
+ wire [AW-1:0] ign_araddr;
+ wire [2:0] ign_arprot;
+ // Verilator lint_on UNUSED
+ // }}}
+
+ axi2axilite #(
+ // {{{
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(SLVDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .OPT_WRITES(1),
+ .OPT_READS(0),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axilwrite (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ // AXI4 slave interface
+ // {{{
+ // Write address channel
+ // {{{
+ .S_AXI_AWVALID(S_AXI_AWVALID),
+ .S_AXI_AWREADY(S_AXI_AWREADY),
+ .S_AXI_AWID( S_AXI_AWID),
+ .S_AXI_AWADDR( S_AXI_AWADDR),
+ .S_AXI_AWLEN( S_AXI_AWLEN),
+ .S_AXI_AWSIZE( S_AXI_AWSIZE),
+ .S_AXI_AWBURST(S_AXI_AWBURST),
+ .S_AXI_AWLOCK( S_AXI_AWLOCK),
+ .S_AXI_AWCACHE(S_AXI_AWCACHE),
+ .S_AXI_AWPROT( S_AXI_AWPROT),
+ .S_AXI_AWQOS( S_AXI_AWQOS),
+ // }}}
+ // Write data channel
+ // {{{
+ .S_AXI_WVALID(S_AXI_WVALID),
+ .S_AXI_WREADY(S_AXI_WREADY),
+ .S_AXI_WDATA( S_AXI_WDATA),
+ .S_AXI_WSTRB( S_AXI_WSTRB),
+ .S_AXI_WLAST( S_AXI_WLAST),
+ // }}}
+ // Write return channel
+ // {{{
+ .S_AXI_BVALID(S_AXI_BVALID),
+ .S_AXI_BREADY(S_AXI_BREADY),
+ .S_AXI_BID( S_AXI_BID),
+ .S_AXI_BRESP( S_AXI_BRESP),
+ // }}}
+ // Read address channel
+ // {{{
+ .S_AXI_ARVALID(1'b0),
+ .S_AXI_ARREADY(ign_arready),
+ .S_AXI_ARID( {(IW){1'b0}}),
+ .S_AXI_ARADDR( {(AW){1'b0}}),
+ .S_AXI_ARLEN( 8'h0),
+ .S_AXI_ARSIZE( 3'h0),
+ .S_AXI_ARBURST(2'h0),
+ .S_AXI_ARLOCK( 1'b0),
+ .S_AXI_ARCACHE(4'h0),
+ .S_AXI_ARPROT( 3'h0),
+ .S_AXI_ARQOS( 4'h0),
+ // }}}
+ // Read data channel
+ // {{{
+ .S_AXI_RVALID(ign_rvalid),
+ .S_AXI_RREADY(1'b1),
+ .S_AXI_RID( ign_rid),
+ .S_AXI_RDATA( ign_rdata),
+ .S_AXI_RRESP( ign_rresp),
+ .S_AXI_RLAST( ign_rlast),
+ // }}}
+ // }}}
+ // AXI-lite master interface
+ // {{{
+ // AXI-lite Write interface
+ // {{{
+ .M_AXI_AWVALID(M_AXI_AWVALID),
+ .M_AXI_AWREADY(M_AXI_AWREADY),
+ .M_AXI_AWADDR(M_AXI_AWADDR),
+ .M_AXI_AWPROT(M_AXI_AWPROT),
+ //
+ .M_AXI_WVALID(M_AXI_WVALID),
+ .M_AXI_WREADY(M_AXI_WREADY),
+ .M_AXI_WDATA(M_AXI_WDATA),
+ .M_AXI_WSTRB(M_AXI_WSTRB),
+ //
+ .M_AXI_BVALID(M_AXI_BVALID),
+ .M_AXI_BREADY(M_AXI_BREADY),
+ .M_AXI_BRESP(M_AXI_BRESP),
+ // }}}
+ // AXI-lite read interface
+ // {{{
+ .M_AXI_ARVALID(ign_arvalid),
+ .M_AXI_ARREADY(1'b1),
+ .M_AXI_ARADDR(ign_araddr),
+ .M_AXI_ARPROT(ign_arprot),
+ //
+ .M_AXI_RVALID(1'b0),
+ .M_AXI_RREADY(ign_rready),
+ .M_AXI_RDATA({(MSTDW){1'b0}}),
+ .M_AXI_RRESP(2'b00)
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ // }}}
+ end else begin : WDN
+ if (OPT_WRITES)
+ begin : IMPLEMENT_WRITES
+ // {{{
+
+ // Register declarations
+ // {{{
+ localparam BIDFIFOBW = IW+1+(SLVSZ-MSTSZ+1);
+
+ //
+ // S_AXI_AW* skid buffer
+ wire skids_awvalid;
+ wire skids_awready;
+ wire [IW-1:0] skids_awid;
+ wire [AW-1:0] skids_awaddr;
+ wire [7:0] skids_awlen;
+ wire [2:0] skids_awsize;
+ wire [1:0] skids_awburst;
+ wire [2:0] skids_awprot;
+ //
+ // S_AXI_W* skid buffer
+ wire skids_wvalid, skids_wready;
+ wire [SLVDW-1:0] skids_wdata;
+ wire [SLVDW/8-1:0] skids_wstrb;
+
+ wire slv_awready, slv_wready;
+ reg [SLVDW-1:0] slv_wdata;
+ reg [SLVDW/8-1:0] slv_wstrb;
+
+ reg [IW-1:0] slv_awid;
+ reg [AW-1:0] slv_awaddr;
+ wire [AW-1:0] slv_next_awaddr;
+ reg [2:0] slv_awsize;
+ reg [1:0] slv_awburst;
+ reg [7:0] slv_awlen;
+ reg [8:0] slv_wlen;
+ reg slv_awlast;
+
+ // Write registers
+ reg m_awvalid;
+ reg bfifo_write;
+ wire wfifo_wlast;
+ reg [IW+1+SLVSZ-MSTSZ:0] bfifo_wdata;
+ wire [LGFIFO:0] wfifo_count;
+ wire wfifo_full;
+ wire wfifo_empty;
+ wire [SLVSZ-MSTSZ:0] wfifo_subcount;
+ wire [IW-1:0] wfifo_bid;
+ reg [SLVSZ-MSTSZ:0] bcounts;
+ reg [IW-1:0] s_axi_bid, bid;
+ reg blast;
+ reg [1:0] s_axi_bresp, bresp;
+ reg s_axi_bvalid;
+ reg b_return_stall;
+ wire read_from_wrfifo;
+ //
+ // S_AXI_B* skid buffer isn't needed
+ //
+ // M_AXI_AW* skid buffer isn't needed
+ //
+ // M_AXI_W* skid buffer isn't needed
+ //
+ // M_AXI_B* skid buffer
+ wire skidm_bvalid, skidm_bready;
+ wire [1:0] skidm_bresp;
+
+ reg m_wvalid;
+ reg [AW-1:0] mst_awaddr;
+ reg [2:0] mst_awprot;
+ reg [SLVSZ-MSTSZ:0] mst_awbeats,
+ mst_wbeats, next_slv_beats;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Incoming write address / data handling
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ ////////////////////////////////////////
+ //
+ // Clock #1: The skid buffer
+ // {{{
+
+ // The write address channel's skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2+3),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0)
+ // }}}
+ ) awskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
+ S_AXI_AWSIZE, S_AXI_AWBURST, S_AXI_AWPROT }),
+ .o_valid(skids_awvalid), .i_ready(skids_awready),
+ .o_data({ skids_awid, skids_awaddr, skids_awlen,
+ skids_awsize, skids_awburst, skids_awprot })
+ // }}}
+ );
+ // }}}
+ //
+ // The write data channel's skid buffer (S_AXI_W*)
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(SLVDW+SLVDW/8),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0)
+ // }}}
+ ) wskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(skids_wvalid), .i_ready(skids_wready),
+ .o_data({ skids_wdata, skids_wstrb })
+ // }}}
+ );
+ // }}}
+
+ assign skids_awready = skids_wvalid && skids_wready
+ && slv_awlast;
+
+ assign skids_wready = slv_wready;
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Clock 2: slv_* clock
+ // {{{
+
+ // When are we ready for a next SLAVE beat?
+ assign slv_awready = (skids_wvalid && skids_wready);
+
+ // slv_aw*
+ // {{{
+ // slv_awaddr is the address of the value *CURRENTLY* in the
+ // buffer, *NOT* the next address.
+ initial slv_awlast = 1;
+ always @(posedge S_AXI_ACLK)
+ if (skids_awvalid && skids_awready)
+ begin
+ slv_awid <= skids_awid;
+ slv_awaddr <= skids_awaddr;
+ slv_awlen <= skids_awlen;
+ slv_awsize <= skids_awsize;
+ slv_awburst <= skids_awburst;
+ end else if (slv_awready && slv_wlen > 0)
+ begin
+ // Step forward a full beat -- but only when we have
+ // the data to do so
+ slv_awaddr <= slv_next_awaddr;
+ end
+ // }}}
+
+ // slv_awlast
+ // {{{
+ initial slv_awlast = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ slv_awlast <= 1;
+ else if (skids_awvalid && skids_awready)
+ slv_awlast <= (skids_awlen == 0);
+ else if (skids_wvalid && skids_wready)
+ slv_awlast <= (slv_wlen <= 2);
+
+`ifdef FORMAL
+ always @(*)
+ assert(slv_awlast == (slv_wlen <= 1));
+`endif
+ // }}}
+
+ // slv_wlen = Number of beats remaining
+ // {{{
+ // ... in the slave's data space, to include the one we've
+ // just ingested. Therefore, this is a 1-up counter. If
+ // slv_wlen == 0, then we are idle.
+ initial slv_wlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ slv_wlen <= 0;
+ else if (skids_awvalid && skids_awready)
+ begin
+ slv_wlen <= skids_awlen + 1;
+ end else if (skids_wvalid && skids_wready) // (slv_awready && slv_wlen > 0)
+ begin
+ slv_wlen <= slv_wlen - 1;
+`ifdef FORMAL
+ assert(slv_wlen > 0);
+`endif
+ end else if (slv_wlen == 1 && slv_wready)
+ slv_wlen <= 0;
+ // }}}
+
+ // next_slv_beats
+ // {{{
+ always @(*)
+ begin
+ // Verilator lint_off WIDTH
+ next_slv_beats = (1<<(skids_awsize-MSTSZ[2:0]))
+ - (skids_awaddr[SLVSZ-1:0] >> skids_awsize);
+ if (skids_awsize <= MSTSZ[2:0])
+ next_slv_beats = 1;
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // slv_next_awaddr
+ // {{{
+ axi_addr #(
+ // {{{
+ .AW(AW),
+ .DW(SLVDW)
+ // }}}
+ ) get_next_slave_addr (
+ // {{{
+ .i_last_addr(slv_awaddr),
+ .i_size(slv_awsize),
+ .i_burst(slv_awburst),
+ .i_len(slv_awlen),
+ .o_next_addr(slv_next_awaddr)
+ // }}}
+ );
+ // }}}
+
+ assign slv_wready = (mst_awbeats <= (M_AXI_AWREADY ? 1:0))
+ && (mst_wbeats <= (M_AXI_WREADY ? 1:0))
+ && (!bfifo_write || !wfifo_full);
+
+ // slv_wstrb, slv_wdata
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ { slv_wstrb, slv_wdata } <= 0;
+ else if (skids_awready)
+ begin
+ slv_wstrb <= skids_wstrb >> (skids_awaddr[SLVSZ-1:MSTSZ]*MSTDW/8);
+ slv_wdata <= skids_wdata >> (skids_awaddr[SLVSZ-1:MSTSZ]*MSTDW);
+ if (OPT_LOWPOWER && !skids_awvalid)
+ begin
+ slv_wstrb <= 0;
+ slv_wdata <= 0;
+ end
+ end else if (skids_wready)
+ begin
+ slv_wstrb <= skids_wstrb >> (slv_next_awaddr[SLVSZ-1:MSTSZ]*MSTDW/8);
+ slv_wdata <= skids_wdata >> (slv_next_awaddr[SLVSZ-1:MSTSZ]*MSTDW);
+ if (OPT_LOWPOWER && !skids_wvalid)
+ begin
+ slv_wstrb <= 0;
+ slv_wdata <= 0;
+ end
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ slv_wstrb <= slv_wstrb >> (MSTDW/8);
+ slv_wdata <= slv_wdata >> (MSTDW);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Clock 2 (continued): mst_* = M_AXI_*
+ // {{{
+
+ // mst_awaddr, mst_awprot, mst_awbeats
+ // {{{
+ initial mst_awaddr = 0;
+ initial mst_awprot = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ if (skids_awvalid && skids_awready)
+ begin
+ mst_awaddr <= skids_awaddr;
+ mst_awprot <= skids_awprot;
+ mst_awbeats<= next_slv_beats;
+ end else if (skids_wvalid && skids_wready)
+ begin
+ mst_awaddr <= slv_next_awaddr;
+ mst_awbeats<= 1;
+ if (slv_awsize >= MSTSZ[2:0])
+ mst_awbeats <= (1<<(slv_awsize - MSTSZ[2:0]));
+ end else begin
+ if (mst_awbeats > 1)
+ begin
+ mst_awaddr <= mst_awaddr + (1<<MSTSZ);
+ mst_awaddr[MSTSZ-1:0] <= 0;
+ end else if (OPT_LOWPOWER)
+ begin
+ mst_awaddr <= 0;
+ end
+
+ if (mst_awbeats > 0)
+ mst_awbeats <= mst_awbeats - 1;
+ end
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ // {{{
+ mst_awbeats <= 0;
+ if (OPT_LOWPOWER)
+ begin
+ mst_awaddr <= 0;
+ mst_awprot <= 0;
+ end
+ // }}}
+ end
+ end
+`ifdef FORMAL
+ always @(*)
+ if(S_AXI_ARESETN && OPT_LOWPOWER && mst_awbeats == 0)
+ begin
+ assert(mst_awaddr == 0);
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(m_axi_awvalid == (mst_awbeats > 0));
+`endif
+ // }}}
+
+ // m_awvalid
+ // {{{
+ initial m_axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ if (skids_wvalid && skids_wready)
+ m_awvalid <= 1'b1;
+ else if (mst_awbeats == 1)
+ m_awvalid <= 1'b0;
+ end
+ // }}}
+
+ assign M_AXI_AWVALID = m_awvalid;
+ assign M_AXI_AWADDR = mst_awaddr;
+ assign M_AXI_AWPROT = mst_awprot;
+
+ // M_AXI_WVALID, mst_wbeats
+ // {{{
+ initial m_wvalid = 0;
+ initial mst_wbeats = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ m_wvalid <= 0;
+ mst_wbeats <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (skids_wvalid && skids_wready)
+ begin
+ m_wvalid <= 1'b1;
+ if (skids_awvalid && skids_awready)
+ mst_wbeats <= next_slv_beats;
+ else if (slv_awsize <= MSTSZ[2:0])
+ mst_wbeats <= 1;
+ else
+ mst_wbeats <= (1<<(slv_awsize - MSTSZ[2:0]));
+ end else begin
+ if (mst_wbeats <= 1)
+ m_wvalid <= 1'b0;
+ if (mst_wbeats > 0)
+ mst_wbeats <= mst_wbeats - 1;
+ end
+ end
+`ifdef FORMAL
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(M_AXI_WVALID == (mst_wbeats > 0));
+`endif
+ // }}}
+
+ // M_AXI_W*
+ // {{{
+ assign M_AXI_WVALID = m_wvalid;
+ assign M_AXI_WDATA = slv_wdata[MSTDW-1:0];
+ assign M_AXI_WSTRB = slv_wstrb[MSTDW/8-1:0];
+ // }}}
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Clock 3: The B FIFO
+ // {{{
+
+ // bfifo_write
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bfifo_write <= 0;
+ else if (!bfifo_write || !wfifo_full)
+ bfifo_write <= skids_wvalid && skids_wready;
+ // }}}
+
+ // bfifo_wdata
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bfifo_wdata <= 0;
+ else if (skids_awvalid && skids_awready)
+ begin
+ bfifo_wdata[SLVSZ-MSTSZ:0] <= next_slv_beats;
+ bfifo_wdata[SLVSZ-MSTSZ+1] <= (skids_awlen == 0);
+ bfifo_wdata[SLVSZ-MSTSZ+2 +: IW] <= skids_awid;
+`ifdef FORMAL
+ assert(skids_wvalid && skids_wready);
+`endif
+ end else if (skids_wvalid && skids_wready)
+ begin
+ bfifo_wdata[SLVSZ-MSTSZ+2 +: IW] <= slv_awid;
+ bfifo_wdata[SLVSZ-MSTSZ+1] <= (slv_wlen <= 2)
+ ? 1'b1 : 1'b0;
+
+ if (slv_awsize <= MSTSZ[2:0])
+ bfifo_wdata[SLVSZ-MSTSZ:0] <= 1;
+ else
+ bfifo_wdata[SLVSZ-MSTSZ:0]
+ <= (1<<(slv_awsize - MSTSZ[2:0]));
+ end
+ // }}}
+
+ // BFIFO
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(BIDFIFOBW), .LGFLEN(LGFIFO)
+ // }}}
+ ) bidlnfifo(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(bfifo_write), .i_data(bfifo_wdata),
+ .o_full(wfifo_full), .o_fill(wfifo_count),
+ .i_rd(read_from_wrfifo),
+ .o_data({ wfifo_bid, wfifo_wlast,wfifo_subcount }),
+ .o_empty(wfifo_empty)
+ // }}}
+ );
+ // }}}
+
+ // read_from_wrfifo
+ // {{{
+ assign read_from_wrfifo = (bcounts <= 1)&&(!wfifo_empty)
+ &&(skidm_bvalid && skidm_bready);
+ // }}}
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Return clock 1: the skid buffer
+ // {{{
+
+ //
+ // The downstream AXI-lite response (M_AXI_B*) skid buffer
+ skidbuffer #(
+ // {{{
+ .DW(2),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0)
+ // }}}
+ ) bskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(M_AXI_BVALID), .o_ready(M_AXI_BREADY),
+ .i_data({ M_AXI_BRESP }),
+ .o_valid(skidm_bvalid), .i_ready(skidm_bready),
+ .o_data({ skidm_bresp })
+ // }}}
+ );
+
+ assign skidm_bready = ((bcounts > 0)||(!wfifo_empty))
+ && !b_return_stall;
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Return staging: Counting returns
+ // {{{
+
+ // bcounts
+ // {{{
+ // Return counts
+ initial bcounts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bcounts <= 0;
+ else if (skidm_bvalid && skidm_bready)
+ begin
+ if (read_from_wrfifo)
+ begin
+ /*
+ if (bcounts == 0 && wfifo_subcount <= 1
+ && wfifo_wlast)
+ // Go straight to S_AXI_BVALID, no
+ // more bursts to count here
+ bcounts <= 0;
+ else
+ */
+ bcounts <= wfifo_subcount + bcounts - 1;
+ end else
+ bcounts <= bcounts - 1;
+ end
+ // }}}
+
+ // blast
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (read_from_wrfifo)
+ blast <= wfifo_wlast;
+ // }}}
+
+ // bid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (read_from_wrfifo)
+ bid <= wfifo_bid;
+ // }}}
+
+ // bresp
+ // {{{
+ initial bresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ bresp <= OKAY;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ bresp <= OKAY;
+ else if (skidm_bvalid && skidm_bready)
+ begin
+ // Let SLVERR take priority over DECERR
+ casez({ bresp, skidm_bresp })
+ 4'b??0?: bresp <= bresp;
+ 4'b0?1?: bresp <= skidm_bresp;
+ 4'b1?10: bresp <= SLVERR;
+ 4'b1011: bresp <= SLVERR;
+ 4'b1111: bresp <= skidm_bresp;
+ endcase
+
+ if (blast)
+ bresp <= OKAY;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////
+ //
+ // Return clock 2: S_AXI_* returns
+ // {{{
+
+ // s_axi_bid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ if (bcounts > 0 && blast)
+ s_axi_bid <= bid;
+ else if (read_from_wrfifo)
+ s_axi_bid <= wfifo_bid;
+ else
+ s_axi_bid <= bid;
+ end
+ // }}}
+
+ // s_axi_bvalid, b_return_stall
+ // {{{
+ always @(*)
+ begin
+ // Force simulation evaluation on reset
+ if (!S_AXI_ARESETN)
+ b_return_stall = 0;
+
+ // Default: stalled if the output is stalled
+ b_return_stall = S_AXI_BVALID && !S_AXI_BREADY;
+
+ if (bcounts > 1)
+ b_return_stall = 0;
+ else if (bcounts == 1 && !blast)
+ b_return_stall = 0;
+ else if (bcounts == 0
+ && (wfifo_subcount > 1 || !wfifo_wlast))
+ b_return_stall = 0;
+ end
+
+ initial s_axi_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_bvalid <= 0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ s_axi_bvalid <= 0;
+ if (skidm_bvalid && skidm_bready)
+ s_axi_bvalid <= ((bcounts == 1)&& blast)
+ ||((bcounts == 0) && (!wfifo_empty)
+ && (wfifo_subcount <= 1)&& wfifo_wlast);
+ end
+ // }}}
+
+ // s_axi_bresp
+ // {{{
+ initial s_axi_bresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_bresp <= OKAY;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ if (skidm_bvalid && skidm_bready)
+ begin
+ // Let SLVERR take priority over DECERR
+ casez({ bresp, skidm_bresp[1] })
+ 3'b??0: s_axi_bresp <= s_axi_bresp;
+ 3'b0?1: s_axi_bresp <= skidm_bresp;
+ 3'b101: s_axi_bresp <= SLVERR;
+ 3'b111: s_axi_bresp <= skidm_bresp;
+ endcase
+ end else
+ s_axi_bresp <= bresp;
+ end
+ // }}}
+
+ // S_AXI_B* assignments
+ // {{{
+ assign S_AXI_BID = s_axi_bid;
+ assign S_AXI_BRESP = s_axi_bresp;
+ assign S_AXI_BVALID = s_axi_bvalid;
+ // }}}
+ // }}}
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_write;
+ assign unused_write = &{ 1'b0, wfifo_count, S_AXI_WLAST };
+ // Verilator lint_on UNUSED
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ //
+ // Formal properties, write half
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // These are only a subset of the formal properties used to
+ // verify this design. While I will warrant that the full
+ // property set will work, I don't really expect the below
+ // properties to be sufficient or for that matter even
+ // syntactically correct.
+ //
+ // Register declarations
+ // {{{
+ localparam F_LGDEPTH = LGFIFO+1+8;
+
+ wire [F_LGDEPTH-1:0] faxi_awr_nbursts;
+ wire [9-1:0] faxi_wr_pending;
+ wire [F_LGDEPTH-1:0] faxi_rd_nbursts;
+ wire [F_LGDEPTH-1:0] faxi_rd_outstanding;
+
+ //
+ // ...
+ //
+
+ localparam F_AXIL_LGDEPTH = F_LGDEPTH;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // AXI channel properties
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(SLVDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .OPT_EXCLUSIVE(0)
+ // ...
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address
+ // {{{
+ .i_axi_awvalid(skids_awvalid),
+ .i_axi_awready(skids_awready),
+ .i_axi_awid( skids_awid),
+ .i_axi_awaddr( skids_awaddr),
+ .i_axi_awlen( skids_awlen),
+ .i_axi_awsize( skids_awsize),
+ .i_axi_awburst(skids_awburst),
+ .i_axi_awlock( 0),
+ .i_axi_awcache(0),
+ .i_axi_awprot( skids_awprot),
+ .i_axi_awqos( 0),
+ // }}}
+ // Write data
+ // {{{
+ .i_axi_wvalid( skids_wvalid),
+ .i_axi_wready( skids_wready),
+ .i_axi_wdata( skids_wdata),
+ .i_axi_wstrb( skids_wstrb),
+ .i_axi_wlast( S_AXI_WLAST),
+ // }}}
+ // Write return response
+ // {{{
+ .i_axi_bvalid( S_AXI_BVALID),
+ .i_axi_bready( S_AXI_BREADY),
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ // }}}
+ // Read address
+ // {{{
+ .i_axi_arvalid(1'b0),
+ .i_axi_arready(1'b0),
+ .i_axi_arid( skids_awid),
+ .i_axi_araddr( skids_awaddr),
+ .i_axi_arlen( skids_awlen),
+ .i_axi_arsize( skids_awsize),
+ .i_axi_arburst(skids_awburst),
+ .i_axi_arlock( 0),
+ .i_axi_arcache(0),
+ .i_axi_arprot( 0),
+ .i_axi_arqos( 0),
+ // }}}
+ // Read response
+ // {{{
+ .i_axi_rvalid( 1'b0),
+ .i_axi_rready( 1'b0),
+ .i_axi_rid( S_AXI_RID),
+ .i_axi_rdata( S_AXI_RDATA),
+ .i_axi_rlast( S_AXI_RLAST),
+ .i_axi_rresp( S_AXI_RRESP),
+ // }}}
+ // Formal property data
+ // {{{
+ .f_axi_awr_nbursts( faxi_awr_nbursts),
+ .f_axi_wr_pending( faxi_wr_pending),
+ .f_axi_rd_nbursts( faxi_rd_nbursts),
+ .f_axi_rd_outstanding(faxi_rd_outstanding)
+ //
+ // ...
+ //
+ // }}}
+ // }}}
+ );
+
+ always @(*)
+ begin
+ // ...
+ assert(faxi_rd_nbursts == 0);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite properties
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxil_master #(
+ // {{{
+ .C_AXI_DATA_WIDTH(MSTDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_NO_RESET(1),
+ .F_AXI_MAXWAIT(5),
+ .F_AXI_MAXDELAY(4),
+ .F_AXI_MAXRSTALL(0),
+ .F_OPT_WRITE_ONLY(1),
+ .F_OPT_READ_ONLY(1'b0),
+ .F_LGDEPTH(F_AXIL_LGDEPTH)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address channel
+ // {{{
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awprot( M_AXI_AWPROT),
+ // }}}
+ // Write data
+ // {{{
+ .i_axi_wvalid( M_AXI_WVALID),
+ .i_axi_wready( M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ // }}}
+ // Write response
+ // {{{
+ .i_axi_bvalid( skidm_bvalid),
+ .i_axi_bready( skidm_bready),
+ .i_axi_bresp( skidm_bresp),
+ // }}}
+ // Read address
+ // {{{
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arprot( M_AXI_ARPROT),
+ // }}}
+ // Read data return
+ // {{{
+ .i_axi_rvalid( 1'b0),
+ .i_axi_rready( 1'b1),
+ .i_axi_rdata( {(C_M_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_rresp( 2'b00),
+ // }}}
+ // Formal check variables
+ // {{{
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ // }}}
+ );
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assert(faxil_rd_outstanding == 0);
+
+ assert(faxil_awr_outstanding
+ + ((mst_awbeats > 0) ? mst_awbeats : 0)
+ == f_wfifo_beats
+ + (bfifo_write ? bfifo_wdata[SLVSZ-MSTSZ:0] : 0)
+ + bcounts);
+
+ assert(faxil_wr_outstanding
+ + ((mst_wbeats > 0) ? mst_wbeats : 0)
+ == f_wfifo_beats
+ + (bfifo_write ? bfifo_wdata[SLVSZ-MSTSZ:0] : 0)
+ + bcounts);
+ end
+
+
+ always @(*)
+ assert(faxil_awr_outstanding <= { 1'b1, {(LGFIFO+8){1'b0}} } + bcounts);
+ always @(*)
+ assert(faxil_wr_outstanding <= { 1'b1, {(LGFIFO+8){1'b0}} } + bcounts);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // BFIFO property checking
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN)
+ begin
+ assert(faxi_awr_nbursts == f_bfifo_packets
+ + (slv_awvalid ? 1:0));
+ assert(f_bfifo_packets <= wfifo_count);
+
+ // ...
+ end
+
+ //
+ // ...
+ //
+
+ // }}}
+`endif
+ // }}}
+ // }}}
+ end else begin : NO_WRITE_SUPPORT
+ // {{{
+ assign S_AXI_AWREADY = 0;
+ assign S_AXI_WREADY = 0;
+ assign S_AXI_BID = 0;
+ assign S_AXI_BRESP = 2'b11;
+ assign S_AXI_BVALID = 0;
+ assign S_AXI_BID = 0;
+
+ //
+ assign M_AXI_AWVALID = 0;
+ assign M_AXI_AWADDR = 0;
+ assign M_AXI_AWPROT = 0;
+ //
+ assign M_AXI_WVALID = 0;
+ assign M_AXI_WDATA = 0;
+ assign M_AXI_WSTRB = 0;
+ //
+ assign M_AXI_BREADY = 0;
+ // }}}
+ end end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_READS && SLVDW == MSTDW)
+ begin : IMPLEMENT_AXI2AXILITE_READS
+ // {{{
+
+ // (Unused) signal declarations
+ // {{{
+ // Verilator lint_off UNUSED
+ wire ign_awready, ign_wready,
+ ign_bvalid;
+ wire [IW-1:0] ign_bid;
+ wire [1:0] ign_bresp;
+
+ wire ign_awvalid, ign_wvalid,
+ ign_bready;
+ wire [AW-1:0] ign_awaddr;
+ wire [2:0] ign_awprot;
+ wire [MSTDW-1:0] ign_wdata;
+ wire [MSTDW/8-1:0] ign_wstrb;
+ // Verilator lint_on UNUSED
+ // }}}
+
+ axi2axilite #(
+ // {{{
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(SLVDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .OPT_WRITES(0),
+ .OPT_READS(1),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axilread (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ // AXI4 slave interface
+ // {{{
+ // Write address channel
+ // {{{
+ .S_AXI_AWVALID(1'b0),
+ .S_AXI_AWREADY(ign_awready),
+ .S_AXI_AWID( {(IW){1'b0}} ),
+ .S_AXI_AWADDR( {(AW){1'b0}} ),
+ .S_AXI_AWLEN( 8'h0),
+ .S_AXI_AWSIZE( 3'h0),
+ .S_AXI_AWBURST(2'h0),
+ .S_AXI_AWLOCK( 1'b0),
+ .S_AXI_AWCACHE(4'h0),
+ .S_AXI_AWPROT( 3'h0),
+ .S_AXI_AWQOS( 4'h0),
+ // }}}
+ // Write data channel
+ // {{{
+ .S_AXI_WVALID(1'b0),
+ .S_AXI_WREADY(ign_wready),
+ .S_AXI_WDATA( {(SLVDW){1'b0}}),
+ .S_AXI_WSTRB( {(SLVDW/8){1'b0}}),
+ .S_AXI_WLAST( 1'b0),
+ // }}}
+ // Write return channel
+ // {{{
+ .S_AXI_BVALID(ign_bvalid),
+ .S_AXI_BREADY(1'b1),
+ .S_AXI_BID( ign_bid),
+ .S_AXI_BRESP( ign_bresp),
+ // }}}
+ // Read address channel
+ // {{{
+ .S_AXI_ARVALID(S_AXI_ARVALID),
+ .S_AXI_ARREADY(S_AXI_ARREADY),
+ .S_AXI_ARID( S_AXI_ARID),
+ .S_AXI_ARADDR( S_AXI_ARADDR),
+ .S_AXI_ARLEN( S_AXI_ARLEN),
+ .S_AXI_ARSIZE( S_AXI_ARSIZE),
+ .S_AXI_ARBURST(S_AXI_ARBURST),
+ .S_AXI_ARLOCK( S_AXI_ARLOCK),
+ .S_AXI_ARCACHE(S_AXI_ARCACHE),
+ .S_AXI_ARPROT( S_AXI_ARPROT),
+ .S_AXI_ARQOS( S_AXI_ARQOS),
+ // }}}
+ // Read data channel
+ // {{{
+ .S_AXI_RVALID(S_AXI_RVALID),
+ .S_AXI_RREADY(S_AXI_RREADY),
+ .S_AXI_RID( S_AXI_RID),
+ .S_AXI_RDATA( S_AXI_RDATA),
+ .S_AXI_RRESP( S_AXI_RRESP),
+ .S_AXI_RLAST( S_AXI_RLAST),
+ // }}}
+ // }}}
+ // AXI-lite master interface
+ // {{{
+ // AXI-lite Write interface
+ // {{{
+ .M_AXI_AWVALID(ign_awvalid),
+ .M_AXI_AWREADY(1'b1),
+ .M_AXI_AWADDR(ign_awaddr),
+ .M_AXI_AWPROT(ign_awprot),
+ //
+ .M_AXI_WVALID(ign_wvalid),
+ .M_AXI_WREADY(1'b1),
+ .M_AXI_WDATA(ign_wdata),
+ .M_AXI_WSTRB(ign_wstrb),
+ //
+ .M_AXI_BVALID(1'b0),
+ .M_AXI_BREADY(ign_bready),
+ .M_AXI_BRESP(2'b00),
+ // }}}
+ // AXI-lite read interface
+ // {{{
+ .M_AXI_ARVALID(M_AXI_ARVALID),
+ .M_AXI_ARREADY(M_AXI_ARREADY),
+ .M_AXI_ARADDR(M_AXI_ARADDR),
+ .M_AXI_ARPROT(M_AXI_ARPROT),
+ //
+ .M_AXI_RVALID(M_AXI_RVALID),
+ .M_AXI_RREADY(M_AXI_RREADY),
+ .M_AXI_RDATA(M_AXI_RDATA),
+ .M_AXI_RRESP(M_AXI_RRESP)
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ // }}}
+ end else begin : RDN
+ if (OPT_READS)
+ begin : IMPLEMENT_READS
+ // {{{
+
+ // Declarations
+ // {{{
+ wire slv_arvalid, slv_arready;
+ reg [IW-1:0] slv_arid, mst_arid;
+ reg [AW-1:0] slv_araddr, mst_araddr;
+ wire [AW-1:0] slv_next_araddr;
+ reg [7:0] slv_arlen;
+ reg slv_arlast, mst_arlast,
+ mst_arsublast;
+ reg [2:0] slv_arprot, mst_arprot;
+ reg [2:0] slv_arsize;
+ reg [1:0] slv_arburst;
+ reg [SLVSZ-MSTSZ:0] slv_arbeats, mst_arbeats;
+ reg [8:0] slv_rlen;
+
+ wire [SLVSZ-MSTSZ-1:0] rfifo_addr;
+ wire rfifo_end_of_beat,
+ rfifo_rlast;
+ wire [4:0] rfifo_count;
+ //
+ reg m_arvalid;
+ wire rfifo_full;
+ wire rfifo_empty;
+ reg s_axi_rvalid;
+ reg [1:0] s_axi_rresp;
+ reg [IW-1:0] s_axi_rid;
+ wire [IW-1:0] rfifo_rid;
+ reg [SLVDW-1:0] s_axi_rdata, next_rdata;
+ reg s_axi_rlast;
+ reg [IW-1:0] rid;
+ wire read_from_rdfifo;
+
+ //
+ //
+ // S_AXI_AR* skid buffer
+ wire skids_arvalid, skids_arready;
+ wire [IW-1:0] skids_arid;
+ wire [AW-1:0] skids_araddr;
+ wire [7:0] skids_arlen;
+ wire [2:0] skids_arsize, skids_arprot;
+ wire [1:0] skids_arburst;
+ //
+ // S_AXI_R* skid buffer isn't needed
+ //
+ // M_AXI_AR* skid buffer isn't needed
+ // M_AXI_R* skid buffer
+ wire skidm_rvalid, skidm_rready;
+ wire [MSTDW-1:0] skidm_rdata;
+ wire [1:0] skidm_rresp;
+ // }}}
+
+ // S_AXI_AR* skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2+3),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0)
+ // }}}
+ ) arskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data({ S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN,
+ S_AXI_ARSIZE, S_AXI_ARBURST, S_AXI_ARPROT }),
+ .o_valid(skids_arvalid), .i_ready(skids_arready),
+ .o_data({ skids_arid, skids_araddr, skids_arlen,
+ skids_arsize, skids_arburst, skids_arprot })
+ // }}}
+ );
+ // }}}
+ // M_AXI_R* skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(MSTDW+2),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0)
+ // }}}
+ ) rskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(M_AXI_RVALID), .o_ready(M_AXI_RREADY),
+ .i_data({ M_AXI_RDATA, M_AXI_RRESP }),
+ .o_valid(skidm_rvalid), .i_ready(skidm_rready),
+ .o_data({ skidm_rdata, skidm_rresp })
+ // }}}
+ );
+ // }}}
+
+ assign skids_arready = (slv_rlen <= (slv_arready ? 1:0))
+ &&(mst_arbeats <=(M_AXI_ARREADY ? 1:0));
+
+ // slv_*
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (OPT_LOWPOWER && (slv_rlen <= (slv_arready ? 1:0)))
+ begin
+ // {{{
+ slv_arid <= 0;
+ slv_araddr <= 0;
+ slv_arlen <= 0;
+ slv_arsize <= 0;
+ slv_arburst <= 0;
+ slv_arprot <= 0;
+
+ slv_rlen <= 0;
+ // }}}
+ end
+
+ if (skids_arvalid && skids_arready)
+ begin
+ // {{{
+ slv_arid <= skids_arid;
+ slv_araddr <= skids_araddr;
+ slv_arlen <= skids_arlen;
+ slv_arsize <= skids_arsize;
+ slv_arburst <= skids_arburst;
+ slv_arlast <= (skids_arlen == 0);
+ slv_arprot <= skids_arprot;
+
+ slv_rlen <= skids_arlen+1;
+ // }}}
+ end else if (slv_arready && slv_rlen > 0)
+ begin
+ // {{{
+ slv_araddr <= (!OPT_LOWPOWER || slv_rlen > 1)
+ ? slv_next_araddr : 0;
+ slv_rlen <= slv_rlen - 1;
+ slv_arlast <= (slv_rlen <= 2);
+ // }}}
+ end
+
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ // {{{
+ slv_arid <= 0;
+ slv_araddr <= 0;
+ slv_arlen <= 0;
+ slv_arsize <= 0;
+ slv_arburst <= 0;
+ slv_arprot <= 0;
+
+ slv_arlast <= 1;
+ // }}}
+ end
+
+ if (!S_AXI_ARESETN)
+ slv_rlen <= 0;
+ end
+
+ assign slv_arvalid = (slv_rlen > 0);
+`ifdef FORMAL
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assert(slv_arlast == (slv_rlen <= 1));
+ assert(slv_rlen <= (slv_arlen + 1));
+ end
+`endif
+ // }}}
+
+ // slv_next_araddr
+ // {{{
+ axi_addr #(
+ // {{{
+ .AW(AW),
+ .DW(SLVDW)
+ // }}}
+ ) get_next_slave_addr (
+ // {{{
+ .i_last_addr(slv_araddr),
+ .i_size(slv_arsize),
+ .i_burst(slv_arburst),
+ .i_len(slv_arlen),
+ .o_next_addr(slv_next_araddr)
+ // }}}
+ );
+ // }}}
+
+ // slv_arbeats
+ // {{{
+ always @(*)
+ if (slv_rlen > 0)
+ begin
+ // Master beats to turn this slave beat into
+ if (slv_arsize >= MSTSZ[2:0])
+ slv_arbeats = (1<<(slv_arsize-MSTSZ[2:0]))
+ - (slv_araddr[MSTSZ-1:0] >> slv_arsize);
+ else
+ slv_arbeats = 1;
+ end else
+ slv_arbeats = 0;
+ // }}}
+
+ // mst_araddr, mst_arprot, mst_arbeats
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (slv_arready)
+ begin
+ // {{{
+ mst_arid <= slv_arid;
+ mst_araddr <= slv_araddr;
+ mst_arprot <= slv_arprot;
+
+ // Beats to turn this beat into
+ mst_arbeats <= slv_arbeats;
+ mst_arlast <= slv_arlast;
+ mst_arsublast <= (slv_arbeats <= 1);
+
+ if (OPT_LOWPOWER && slv_rlen == 0)
+ begin
+ mst_arid <= 0;
+ mst_araddr <= 0;
+ mst_arprot <= 0;
+ end
+ // }}}
+ end else if ((mst_arbeats > 0)
+ &&(M_AXI_ARVALID && M_AXI_ARREADY))
+ begin
+ // {{{
+ mst_araddr <= mst_araddr + (1<<MSTSZ);
+ mst_araddr[MSTSZ-1:0] <= 0;
+ mst_arbeats <= mst_arbeats - 1;
+
+ mst_arsublast <= (mst_arbeats <= 2);
+ // }}}
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ // {{{
+ mst_arbeats <= 0;
+ if (OPT_LOWPOWER)
+ begin
+ mst_arid <= 0;
+ mst_araddr <= 0;
+ mst_arprot <= 0;
+ end
+ // }}}
+ end
+ end
+`ifdef FORMAL
+ always @(*)
+ if(OPT_LOWPOWER && S_AXI_ARESETN && mst_arbeats == 0)
+ begin
+ assert(mst_arid == 0);
+ assert(mst_araddr == 0);
+ assert(mst_arprot == 0);
+ end
+`endif
+ // }}}
+
+ // m_arvalid
+ // {{{
+ initial m_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_arvalid <= 0;
+ else if (slv_arvalid && slv_arready)
+ m_arvalid <= 1;
+ else if (M_AXI_ARVALID && M_AXI_ARREADY)
+ m_arvalid <= (mst_arbeats > 1);
+`ifdef FORMAL
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(M_AXI_ARVALID == (mst_arbeats > 0));
+`endif
+ // }}}
+
+ assign slv_arready = (mst_arbeats <= (M_AXI_ARREADY ? 1:0));
+ assign read_from_rdfifo = skidm_rvalid && skidm_rready
+ &&(!S_AXI_RVALID || S_AXI_RREADY);
+
+ // Read ID FIFO
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(IW+2+SLVSZ-MSTSZ),
+ .LGFLEN(LGFIFO)
+ // }}}
+ ) ridlnfifo(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ //
+ .i_wr(M_AXI_ARVALID && M_AXI_ARREADY),
+ .i_data({ mst_arid, mst_arlast, mst_arsublast,
+ M_AXI_ARADDR[SLVSZ-1:MSTSZ] }),
+ .o_full(rfifo_full), .o_fill(rfifo_count),
+ //
+ .i_rd(read_from_rdfifo),
+ .o_data({ rfifo_rid, rfifo_rlast, rfifo_end_of_beat,
+ rfifo_addr }),
+ .o_empty(rfifo_empty)
+ // }}}
+ );
+ // }}}
+
+ assign skidm_rready = (!S_AXI_RVALID || S_AXI_RREADY)
+ && !rfifo_empty;
+
+ // s_axi_rvalid
+ // {{{
+ initial s_axi_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_rvalid <= 0;
+ else if (skidm_rvalid && skidm_rready && rfifo_end_of_beat)
+ s_axi_rvalid <= 1'b1;
+ else if (S_AXI_RREADY)
+ s_axi_rvalid <= 0;
+ // }}}
+
+ // s_axi_rdata
+ // {{{
+ always @(*)
+ begin
+ next_rdata = s_axi_rdata;
+ if (S_AXI_RVALID)
+ next_rdata = 0;
+ if (skidm_rvalid)
+ next_rdata = next_rdata | ({ {(SLVDW-MSTDW){1'b0}}, skidm_rdata } << (rfifo_addr * MSTDW));
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ s_axi_rdata <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ s_axi_rdata <= next_rdata;
+ // }}}
+
+ // s_axi_rresp
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_axi_rresp <= OKAY;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (S_AXI_RVALID)
+ s_axi_rresp <= (skidm_rvalid) ? skidm_rresp : OKAY;
+ else if (skidm_rvalid)
+ casez({ s_axi_rresp, skidm_rresp[1] })
+ // Let SLVERR take priority over DECERR
+ 3'b??0: s_axi_rresp <= s_axi_rresp;
+ 3'b0?1: s_axi_rresp <= skidm_rresp;
+ 3'b101: s_axi_rresp <= SLVERR;
+ 3'b111: s_axi_rresp <= skidm_rresp;
+ endcase
+ end
+ // }}}
+
+ // rid
+ // {{{
+ initial rid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (read_from_rdfifo)
+ rid <= rfifo_rid;
+ // }}}
+
+ // s_axi_rlast
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (read_from_rdfifo)
+ s_axi_rlast <= rfifo_rlast;
+ else
+ s_axi_rlast <= 0;
+ end
+ // }}}
+
+ // s_axi_rid
+ // {{{
+ initial s_axi_rid = 0;
+ always @(posedge S_AXI_ACLK)
+ if ((S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ ||(!S_AXI_RVALID && rfifo_end_of_beat))
+ s_axi_rid <= (read_from_rdfifo && rfifo_end_of_beat)?rfifo_rid : rid;
+ // }}}
+
+ // M_AXI_AR*
+ // {{{
+ assign M_AXI_ARVALID= m_arvalid;
+ assign M_AXI_ARADDR = mst_araddr;
+ assign M_AXI_ARPROT = mst_arprot;
+ // }}}
+ // S_AXI_R*
+ // {{{
+ assign S_AXI_RVALID = s_axi_rvalid;
+ assign S_AXI_RDATA = s_axi_rdata;
+ assign S_AXI_RRESP = s_axi_rresp;
+ assign S_AXI_RLAST = s_axi_rlast;
+ assign S_AXI_RID = s_axi_rid;
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_read;
+ assign unused_read = &{ 1'b0,
+ /*
+ S_AXI_AWID, S_AXI_AWVALID,
+ S_AXI_AWLEN, S_AXI_AWBURST, S_AXI_AWSIZE,
+ S_AXI_AWADDR,
+ S_AXI_WVALID, S_AXI_WDATA, S_AXI_WSTRB,
+ S_AXI_WLAST, S_AXI_BREADY,
+ M_AXI_AWREADY, M_AXI_WREADY,
+ M_AXI_BRESP, M_AXI_BVALID,
+ */
+ rfifo_count, rfifo_full
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ //
+ // Formal properties, read half
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // As with the write half, the following is a subset of the
+ // formal properties used to verify this section of the core.
+ // It may, or may not, be syntactically correct. I don't
+ // warrant this version of the design.
+ //
+ // Register declarations
+ // {{{
+ localparam F_LGDEPTH = LGFIFO+1+8;
+
+ wire [F_LGDEPTH-1:0] faxi_awr_nbursts;
+ wire [9-1:0] faxi_wr_pending;
+ wire [F_LGDEPTH-1:0] faxi_rd_nbursts;
+ wire [F_LGDEPTH-1:0] faxi_rd_outstanding;
+
+ //
+ // ...
+ //
+
+ localparam F_AXIL_LGDEPTH = F_LGDEPTH;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // AXI channel properties
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(SLVDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .OPT_EXCLUSIVE(0)
+ // ...
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address
+ // {{{
+ .i_axi_awvalid(1'b0),
+ .i_axi_awready(1'b0),
+ .i_axi_awid( skids_arid),
+ .i_axi_awaddr( skids_araddr),
+ .i_axi_awlen( 8'h0),
+ .i_axi_awsize( 3'h0),
+ .i_axi_awburst(2'h0),
+ .i_axi_awlock( 0),
+ .i_axi_awcache(0),
+ .i_axi_awprot( 0),
+ .i_axi_awqos( 0),
+ // }}}
+ // Write data
+ // {{{
+ .i_axi_wvalid( 1'b0),
+ .i_axi_wready( 1'b0),
+ .i_axi_wdata( {(C_S_AXI_DATA_WIDTH ){1'b0}}),
+ .i_axi_wstrb( {(C_S_AXI_DATA_WIDTH/8){1'b0}}),
+ .i_axi_wlast( 1'b0),
+ // }}}
+ // Write return response
+ // {{{
+ .i_axi_bvalid( 1'b0),
+ .i_axi_bready( 1'b0),
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( 2'b00),
+ // }}}
+ // Read address
+ // {{{
+ .i_axi_arready(skids_arready),
+ .i_axi_arid( skids_arid),
+ .i_axi_araddr( skids_araddr),
+ .i_axi_arlen( skids_arlen),
+ .i_axi_arsize( skids_arsize),
+ .i_axi_arburst(skids_arburst),
+ .i_axi_arlock( 0),
+ .i_axi_arcache(0),
+ .i_axi_arprot( 0),
+ .i_axi_arqos( 0),
+ .i_axi_arvalid(skids_arvalid),
+ // }}}
+ // Read response
+ // {{{
+ .i_axi_rid( S_AXI_RID),
+ .i_axi_rresp( S_AXI_RRESP),
+ .i_axi_rvalid( S_AXI_RVALID),
+ .i_axi_rdata( S_AXI_RDATA),
+ .i_axi_rlast( S_AXI_RLAST),
+ .i_axi_rready( S_AXI_RREADY),
+ // }}}
+ // Formal property data
+ // {{{
+ .f_axi_awr_nbursts( faxi_awr_nbursts),
+ .f_axi_wr_pending( faxi_wr_pending),
+ .f_axi_rd_nbursts( faxi_rd_nbursts),
+ .f_axi_rd_outstanding(faxi_rd_outstanding),
+ //
+ // ...
+ //
+ // }}}
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxi_awr_nbursts == 0);
+ assert(faxi_wr_pending == 0);
+ assert(faxi_wr_ckvalid == 0);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite properties
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ faxil_master #(
+ // {{{
+ .C_AXI_DATA_WIDTH(MSTDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_NO_RESET(1),
+ .F_AXI_MAXWAIT(5),
+ .F_AXI_MAXDELAY(4),
+ .F_AXI_MAXRSTALL(0),
+ .F_OPT_WRITE_ONLY(1'b0),
+ .F_OPT_READ_ONLY(1'b1),
+ .F_LGDEPTH(F_AXIL_LGDEPTH)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ // Write address channel
+ // {{{
+ .i_axi_awvalid(1'b0),
+ .i_axi_awready(1'b0),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awprot( 3'h0),
+ // }}}
+ // Write data
+ // {{{
+ .i_axi_wvalid( 1'b0),
+ .i_axi_wready( 1'b0),
+ .i_axi_wdata( {(C_M_AXI_DATA_WIDTH ){1'b0}}),
+ .i_axi_wstrb( {(C_M_AXI_DATA_WIDTH/8){1'b0}}),
+ // }}}
+ // Write response
+ // {{{
+ .i_axi_bvalid( 1'b0),
+ .i_axi_bready( 1'b0),
+ .i_axi_bresp( 2'b00),
+ // }}}
+ // Read address
+ // {{{
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arprot( M_AXI_ARPROT),
+ // }}}
+ // Read data return
+ // {{{
+ .i_axi_rvalid( skidm_rvalid),
+ .i_axi_rready( skidm_rready),
+ .i_axi_rdata( skidm_rdata),
+ .i_axi_rresp( skidm_rresp),
+ // }}}
+ // Formal check variables
+ // {{{
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_awr_outstanding == 0);
+ assert(faxil_wr_outstanding == 0);
+ end
+ // }}}
+`endif
+ // }}}
+ // }}}
+ end else begin : NO_READ_SUPPORT // if (!OPT_READS)
+ // {{{
+ assign M_AXI_ARVALID= 0;
+ assign M_AXI_ARADDR = 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_RREADY = 0;
+ //
+ assign S_AXI_ARREADY= 0;
+ assign S_AXI_RVALID = 0;
+ assign S_AXI_RDATA = 0;
+ assign S_AXI_RRESP = 0;
+ assign S_AXI_RLAST = 0;
+ assign S_AXI_RID = 0;
+
+ // Make Verilator happy
+ // Verilator lint_off UNUSED
+ wire unused_read;
+ assign unused_read = &{ 1'b0, M_AXI_ARREADY, M_AXI_RVALID,
+ M_AXI_RDATA, M_AXI_RRESP, S_AXI_RREADY,
+ S_AXI_ARLEN, S_AXI_ARSIZE, S_AXI_ARBURST,
+ S_AXI_ARADDR, S_AXI_ARVALID, S_AXI_ARID
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+ end end endgenerate
+ // }}}
+ // Minimal parameter validation
+ // {{{
+ initial begin
+ if (SLVDW < MSTDW)
+ begin
+ $fatal; // Fatal elaboration error
+ $stop; // Stop any simulation
+ end
+ end
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+ S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS,
+ // skids_wlast, wfifo_count, rfifo_count
+ S_AXI_ARLOCK, S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assume that the two write channels stay within an appropriate
+ // distance of each other. This is to make certain that the property
+ // file features are not violated, although not necessary true for
+ // actual operation
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Select only write or only read operation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (!OPT_WRITES)
+ begin
+ always @(*)
+ begin
+ assume(!S_AXI_AWVALID);
+ assume(!S_AXI_WVALID);
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assume(!M_AXI_BVALID);
+ assert(!S_AXI_BVALID);
+ end
+ end endgenerate
+
+ generate if (!OPT_READS)
+ begin
+
+ always @(*)
+ begin
+ assume(!S_AXI_ARVALID);
+ assert(!M_AXI_ARVALID);
+ end
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Lowpower assertions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_LOWPOWER)
+ begin : F_LOWPOWER
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (!M_AXI_AWVALID)
+ begin
+ // Not supported.
+ // assert(M_AXI_AWADDR == 0);
+ // assert(M_AXI_AWPROT == 0);
+ end
+
+ if (!M_AXI_WVALID)
+ begin
+ // assert(M_AXI_WDATA == 0);
+ // assert(M_AXI_WSTRB == 0);
+ end
+
+ if (!M_AXI_ARVALID)
+ begin
+ assert(M_AXI_ARADDR == 0);
+ assert(M_AXI_ARPROT == 0);
+ end
+
+ if (!S_AXI_RVALID)
+ begin
+ // These items build over the course of a
+ // returned burst, so they might not be
+ // zero when RVALID is zero.
+ //
+ // assert(S_AXI_RLAST == 0);
+ // assert(S_AXI_RDATA == 0);
+ // assert(S_AXI_RID == 0);
+ end
+ end
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover statements, to show performance
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_READS)
+ begin
+ // {{{
+ reg [3:0] cvr_read_count, cvr_read_count_simple;
+
+ initial cvr_read_count_simple = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_count_simple <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 0)
+ cvr_read_count_simple <= cvr_read_count_simple + 1;
+
+ initial cvr_read_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_count <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 2)
+ cvr_read_count <= cvr_read_count + 1;
+
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+
+ // }}}
+`undef BMC_ASSERT
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axi32axi.v b/rtl/wb2axip/axi32axi.v
new file mode 100644
index 0000000..fd1badb
--- /dev/null
+++ b/rtl/wb2axip/axi32axi.v
@@ -0,0 +1,376 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi32axi.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Bridge from an AXI3 slave to an AXI4 master
+//
+// The goal here is to support as high a bus speed as possible, maintain
+// burst support (if possible) and (more important) allow bus requests
+// coming from the ARM within either the Zynq or one of Intel's SOC chips
+// to speak with an AutoFPGA based design.
+//
+// Note that if you aren't using AutoFPGA, then you probably don't need
+// this core--the vendor tools should be able to handle this conversion
+// quietly and automatically for you.
+//
+// Notes:
+// AxCACHE is remapped as per the AXI4 specification, since the values
+// aren't truly equivalent. This forces a single clock delay in the Ax*
+// channels and (likely) the W* channel as well as a system level
+// consequence.
+//
+// AXI3 locking is not supported under AXI4. As per the AXI4 spec,
+// AxLOCK is converteted from AXI3 to AXI4 by just dropping the high
+// order bit.
+//
+// The WID input is ignored. Whether or not this input can be ignored
+// is based upon how the ARM is implemented internally. After a bit
+// of research into both Zynq's and Intel SOCs, this appears to be the
+// appropriate answer here.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axi32axi #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter OPT_REORDER_METHOD = 0,
+ parameter [0:0] OPT_TRANSFORM_AXCACHE = 1,
+ parameter [0:0] OPT_LOWPOWER = 0,
+ parameter [0:0] OPT_LOW_LATENCY = 0,
+ parameter WID_LGAWFIFO = 3,
+ parameter WID_LGWFIFO = 3
+ //
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI3 incoming/slave interface
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [3:0] S_AXI_AWLEN,
+ input wire [2:0] S_AXI_AWSIZE,
+ input wire [1:0] S_AXI_AWBURST,
+ input wire [1:0] S_AXI_AWLOCK,
+ input wire [3:0] S_AXI_AWCACHE,
+ input wire [2:0] S_AXI_AWPROT,
+ input wire [3:0] S_AXI_AWQOS,
+ //
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_WID,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ //
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [3:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire [1:0] S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire S_AXI_RLAST,
+ output wire [1:0] S_AXI_RRESP,
+ //
+ //
+ // The AXI4 Master (outgoing) interface
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [7:0] M_AXI_AWLEN,
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ //
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [7:0] M_AXI_ARLEN,
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ );
+
+ // Register/net declarations
+ // {{{
+ // localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam IW=C_AXI_ID_WIDTH;
+ reg [3:0] axi4_awcache, axi4_arcache;
+ reg axi4_awlock, axi4_arlock;
+ wire awskd_ready;
+ wire wid_reorder_awready;
+ wire [IW-1:0] reordered_wid;
+ // }}}
+
+ // Write cache remapping
+ // {{{
+ always @(*)
+ case(S_AXI_AWCACHE)
+ 4'b1010: axi4_awcache = 4'b1110;
+ 4'b1011: axi4_awcache = 4'b1111;
+ default: axi4_awcache = S_AXI_AWCACHE;
+ endcase
+ // }}}
+
+ // AWLOCK
+ // {{{
+ always @(*)
+ axi4_awlock = S_AXI_AWLOCK[0];
+ // }}}
+
+ // AW Skid buffer
+ // {{{
+ generate if (OPT_TRANSFORM_AXCACHE)
+ begin : GEN_AWCACHE
+ // {{{
+ skidbuffer #(
+ .DW(C_AXI_ADDR_WIDTH + C_AXI_ID_WIDTH
+ + 4 + 3 + 2 + 1+4+3+4),
+ .OPT_OUTREG(1'b1)
+ ) awskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID && wid_reorder_awready),
+ .o_ready(awskd_ready),
+ .i_data({ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
+ S_AXI_AWSIZE, S_AXI_AWBURST,axi4_awlock,
+ axi4_awcache, S_AXI_AWPROT, S_AXI_AWQOS
+ }),
+ .o_valid(M_AXI_AWVALID), .i_ready(M_AXI_AWREADY),
+ .o_data({ M_AXI_AWID, M_AXI_AWADDR,
+ M_AXI_AWLEN[3:0],
+ M_AXI_AWSIZE,M_AXI_AWBURST,M_AXI_AWLOCK,
+ M_AXI_AWCACHE,M_AXI_AWPROT, M_AXI_AWQOS
+ })
+ );
+
+ assign M_AXI_AWLEN[7:4] = 4'h0;
+ assign S_AXI_AWREADY = awskd_ready && wid_reorder_awready;
+ // }}}
+ end else begin : IGN_AWCACHE
+ // {{{
+ assign M_AXI_AWVALID = S_AXI_AWVALID && wid_reorder_awready;
+ assign S_AXI_AWREADY = M_AXI_AWREADY;
+ assign M_AXI_AWID = S_AXI_AWID;
+ assign M_AXI_AWADDR = S_AXI_AWADDR;
+ assign M_AXI_AWLEN = { 4'h0, S_AXI_AWLEN };
+ assign M_AXI_AWSIZE = S_AXI_AWSIZE;
+ assign M_AXI_AWBURST = S_AXI_AWBURST;
+ assign M_AXI_AWLOCK = axi4_awlock;
+ assign M_AXI_AWCACHE = axi4_awcache;
+ assign M_AXI_AWPROT = S_AXI_AWPROT;
+ assign M_AXI_AWQOS = S_AXI_AWQOS;
+
+ assign awskd_ready = 1;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Handle write channel de-interleaving
+ // {{{
+ axi3reorder #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_METHOD(OPT_REORDER_METHOD),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_LOW_LATENCY(OPT_LOW_LATENCY),
+ .LGAWFIFO(WID_LGAWFIFO),
+ .LGWFIFO(WID_LGWFIFO)
+ // }}}
+ ) widreorder (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK), .S_AXI_ARESETN(S_AXI_ARESETN),
+ // Incoming Write address ID
+ .S_AXI3_AWVALID(S_AXI_AWVALID && S_AXI_AWREADY),
+ .S_AXI3_AWREADY(wid_reorder_awready),
+ .S_AXI3_AWID(S_AXI_AWID),
+ // Incoming Write data info
+ .S_AXI3_WVALID(S_AXI_WVALID),
+ .S_AXI3_WREADY(S_AXI_WREADY),
+ .S_AXI3_WID(S_AXI_WID),
+ .S_AXI3_WDATA(S_AXI_WDATA),
+ .S_AXI3_WSTRB(S_AXI_WSTRB),
+ .S_AXI3_WLAST(S_AXI_WLAST),
+ // Outgoing write data channel
+ .M_AXI_WVALID(M_AXI_WVALID),
+ .M_AXI_WREADY(M_AXI_WREADY),
+ .M_AXI_WID(reordered_wid),
+ .M_AXI_WDATA(M_AXI_WDATA),
+ .M_AXI_WSTRB(M_AXI_WSTRB),
+ .M_AXI_WLAST(M_AXI_WLAST)
+ // }}}
+ );
+ // }}}
+
+ // Forward the B* channel return
+ // {{{
+ assign S_AXI_BVALID = M_AXI_BVALID;
+ assign M_AXI_BREADY = S_AXI_BREADY;
+ assign S_AXI_BID = M_AXI_BID;
+ assign S_AXI_BRESP = M_AXI_BRESP;
+ // }}}
+
+ // Read cache remapping
+ // {{{
+ always @(*)
+ case(S_AXI_ARCACHE)
+ 4'b0110: axi4_arcache = 4'b1110;
+ 4'b0111: axi4_arcache = 4'b1111;
+ default: axi4_arcache = S_AXI_ARCACHE;
+ endcase
+ // }}}
+
+ // ARLOCK
+ // {{{
+ always @(*)
+ axi4_arlock = S_AXI_ARLOCK[0];
+ // }}}
+
+ // AR Skid buffer
+ // {{{
+ generate if (OPT_TRANSFORM_AXCACHE)
+ begin : GEN_ARCACHE
+ // {{{
+ skidbuffer #(
+ .DW(C_AXI_ADDR_WIDTH + C_AXI_ID_WIDTH
+ + 4 + 3 + 2 + 1+4+3+4),
+ .OPT_OUTREG(1'b1)
+ ) arskid (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data({ S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN,
+ S_AXI_ARSIZE, S_AXI_ARBURST, axi4_arlock,
+ axi4_arcache, S_AXI_ARPROT, S_AXI_ARQOS }),
+ .o_valid(M_AXI_ARVALID), .i_ready(M_AXI_ARREADY),
+ .o_data({ M_AXI_ARID, M_AXI_ARADDR, M_AXI_ARLEN[3:0],
+ M_AXI_ARSIZE, M_AXI_ARBURST, M_AXI_ARLOCK,
+ M_AXI_ARCACHE, M_AXI_ARPROT, M_AXI_ARQOS })
+ );
+
+ assign M_AXI_ARLEN[7:4] = 4'h0;
+ // }}}
+ end else begin : IGN_ARCACHE
+ // {{{
+ assign M_AXI_ARVALID = S_AXI_ARVALID;
+ assign S_AXI_ARREADY = M_AXI_ARREADY;
+ assign M_AXI_ARID = S_AXI_ARID;
+ assign M_AXI_ARADDR = S_AXI_ARADDR;
+ assign M_AXI_ARLEN = { 4'h0, S_AXI_ARLEN };
+ assign M_AXI_ARSIZE = S_AXI_ARSIZE;
+ assign M_AXI_ARBURST = S_AXI_ARBURST;
+ assign M_AXI_ARLOCK = axi4_arlock;
+ assign M_AXI_ARCACHE = axi4_arcache;
+ assign M_AXI_ARPROT = S_AXI_ARPROT;
+ assign M_AXI_ARQOS = S_AXI_ARQOS;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Forward the R* channel return
+ // {{{
+ assign S_AXI_RVALID = M_AXI_RVALID;
+ assign M_AXI_RREADY = S_AXI_RREADY;
+ assign S_AXI_RID = M_AXI_RID;
+ assign S_AXI_RDATA = M_AXI_RDATA;
+ assign S_AXI_RLAST = M_AXI_RLAST;
+ assign S_AXI_RRESP = M_AXI_RRESP;
+ // }}}
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWLOCK[1], S_AXI_ARLOCK[1],
+ reordered_wid };
+ // Verilator lint_on UNUSED
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+//
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+//
+// This design has not been formally verified.
+//
+`endif
+endmodule
diff --git a/rtl/wb2axip/axi3reorder.v b/rtl/wb2axip/axi3reorder.v
new file mode 100644
index 0000000..3c92c0d
--- /dev/null
+++ b/rtl/wb2axip/axi3reorder.v
@@ -0,0 +1,743 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi3reorder.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: One of the challenges of working with AXI3 are the (potentially)
+// out of order writes. This modules is designed to re-order write
+// beats back into the proper order given by the AW* channel. Once done,
+// write beats will always be contiguous--only changing ID's after WLAST.
+// Indeed, once done, the WID field can be properly removed and ignored.
+// it's left within for forms sake, but isn't required.
+//
+// Algorithms:
+// The design currently contains one of three reordering algorithms.
+//
+// MTHD_NONE Is a basic pass-through. This would be appropriate
+// for AXI3 streams that are known to be in order already.
+//
+// MTHD_SHIFT_REGISTER Uses a shift-register based "FIFO". (Not block
+// RAM) All write data goes into this FIFO. Items
+// are read from this FIFO out of order as required for
+// the given ID. When any item is read from the middle,
+// the shift register back around the item read.
+//
+// This is a compromise implementation that uses less
+// logic than the PER/ID FIFOS, while still allowing some
+// amount of reordering to take place.
+//
+// MTHD_PERID_FIFOS Uses an explicit FIFO for each ID. Data come
+// into the core and go directly into the FIFO's.
+// Data are read out of the FIFOs in write-address order
+// until WLAST, where the write address may (potentially)
+// change.
+//
+// For a small number of IDs, this solution should be
+// *complete*, and lacking nothing. (Unless you fill the
+// write data FIFO's while needing data from another ID ..)
+//
+// Implementation notes:
+// This module is intended to be used side by side with other AW* channel
+// processing, and following an (external) AW* skidbuffer. For this
+// reason, it doesn't use an AW skid buffer, nor does it output AW*
+// information. External AWREADY handling should therefore be:
+//
+// master_awskid_read = reorder_awready && any_other_awready;
+//
+// going into a skid buffer, with the proper AWREADY being the upstream
+// skidbuffer's AWREADY output.
+//
+// Expected performance:
+// One beat per clock, all methods.
+//
+// Status:
+// This module passes both a Verilator lint check and a 15-20 step formal
+// bounded model check.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axi3reorder #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 4,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter LGAWFIFO = 3, // # packets we can handle
+ parameter LGWFIFO = 4, // Full size of an AXI burst
+ parameter OPT_METHOD = 0,
+ parameter [0:0] OPT_LOWPOWER = 0,
+ parameter [0:0] OPT_LOW_LATENCY = 0,
+ parameter NUM_FIFOS = (1<<C_AXI_ID_WIDTH)
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI3 (partial) slave interface
+ // {{{
+ input wire S_AXI3_AWVALID,
+ output wire S_AXI3_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI3_AWID,
+ // input wire [AW-1:0] S_AXI3_AWADDR,
+ // input wire [3:0] S_AXI3_AWLEN,
+ // input wire [2:0] S_AXI3_AWSIZE,
+ // input wire [1:0] S_AXI3_AWBURST,
+ // input wire [1:0] S_AXI3_AWLOCK,
+ // input wire [3:0] S_AXI3_AWCACHE,
+ // input wire [2:0] S_AXI3_AWPROT,
+ // input wire [3:0] S_AXI3_AWQOS,
+ //
+ input wire S_AXI3_WVALID,
+ output wire S_AXI3_WREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI3_WID,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI3_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI3_WSTRB,
+ input wire S_AXI3_WLAST,
+ // }}}
+ // Reordered write data port. WID may now be discarded.
+ // {{{
+ output reg M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output reg [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+ output reg [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output reg [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output reg M_AXI_WLAST
+ // }}}
+ // }}}
+ );
+
+ // Register declarations
+ // {{{
+ localparam MTHD_NONE = 0;
+ localparam MTHD_SHIFT_REGISTER = 1;
+ localparam MTHD_PERID_FIFOS = 2;
+ localparam IW = C_AXI_ID_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ wire awfifo_full, awfifo_empty;
+ wire read_awid_fifo;
+ wire [IW-1:0] awfifo_id;
+ wire [LGAWFIFO:0] awfifo_fill;
+
+ genvar gk;
+ reg read_beat_fifo;
+
+ reg cid_valid;
+ reg [IW-1:0] current_id;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write address ID's go to an address ID FIFO
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ generate if (OPT_METHOD == MTHD_NONE)
+ begin : IGNORE_AW_CHANNEL
+ // No write address ID's to keep track of
+ // {{{
+ // These values are irrelevant. They are placeholders, put here
+ // to keep the linter from complaining.
+
+ assign awfifo_id = S_AXI3_AWID;
+ assign S_AXI3_AWREADY = 1'b1;
+ assign awfifo_full = 0;
+ assign awfifo_fill = 0;
+ assign awfifo_empty = !S_AXI3_AWVALID;
+ always @(*)
+ cid_valid = S_AXI3_AWVALID;
+ always @(*)
+ current_id= S_AXI3_AWID;
+ assign read_awid_fifo = 0;
+
+ // Verilator lint_off UNUSED
+ wire none_aw_unused;
+ assign none_aw_unused = &{ 1'b0, awfifo_full, awfifo_fill,
+ awfifo_empty, read_awid_fifo, awfifo_id };
+ // }}}
+ end else begin : AWFIFO
+ ////////////////////////////////////////////////////////////////
+ //
+ // Write address ID FIFO
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ sfifo #(
+ // {{{
+ .BW(IW),
+ .LGFLEN(LGAWFIFO),
+ .OPT_READ_ON_EMPTY(OPT_LOW_LATENCY)
+ // }}}
+ ) awidfifo(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(S_AXI3_AWVALID && S_AXI3_AWREADY),
+ .i_data(S_AXI3_AWID),
+ .o_full(awfifo_full),
+ .o_fill(awfifo_fill),
+ .i_rd(read_awid_fifo),
+ .o_data( awfifo_id ),
+ .o_empty(awfifo_empty)
+ // }}}
+ );
+
+ assign S_AXI3_AWREADY = !awfifo_full;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Current ID -- what ID shall we output next?
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ // The current ID is given by the write address channel
+
+ initial cid_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cid_valid <= 1'b0;
+ else if (read_awid_fifo)
+ cid_valid <= !awfifo_empty;
+
+ // The current ID is given by the write address channel
+ always @(posedge S_AXI_ACLK)
+ if (read_awid_fifo)
+ current_id <= awfifo_id;
+
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write data channel processing and reordering
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_METHOD == MTHD_NONE)
+ begin : COPY_SW_TO_MW // Copy S_AXI3_W* values to M_AXIW*
+ // {{{
+ // {{{
+ always @(*)
+ begin
+ M_AXI_WVALID = S_AXI3_WVALID;
+ M_AXI_WID = S_AXI3_WID;
+ M_AXI_WDATA = S_AXI3_WDATA;
+ M_AXI_WSTRB = S_AXI3_WSTRB;
+ M_AXI_WLAST = S_AXI3_WLAST;
+
+ read_beat_fifo = 0;
+ end
+ // }}}
+
+ assign S_AXI3_WREADY = M_AXI_WREADY;
+
+ // Keep Verilator happy
+ // Verilator lint_off UNUSED
+ wire none_unused;
+ assign none_unused = &{ 1'b0, S_AXI_ACLK, S_AXI_ARESETN,
+ current_id, cid_valid, read_beat_fifo };
+ // Verilator lint_on UNUSED
+ // }}}
+ end else if (OPT_METHOD == MTHD_SHIFT_REGISTER)
+ begin : SHIFT_REGISTER
+ // {{{
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Local declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam NSREG = (1<<LGWFIFO);
+ reg [NSREG-1:0] sr_advance;
+ reg [NSREG-1:0] sr_write;
+ reg [NSREG-1:0] sr_valid;
+ reg [IW-1:0] sr_id [0:NSREG];
+ reg [DW-1:0] sr_data [0:NSREG];
+ reg [DW/8-1:0] sr_strb [0:NSREG];
+ reg [NSREG-1:0] sr_last;
+
+ integer ik;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Per-register-station logic
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ for (gk = 0; gk<NSREG; gk=gk+1)
+ begin
+
+ // sr_advance
+ // {{{
+ // Do we copy from the next station into this one or no?
+ always @(*)
+ begin
+ sr_advance[gk] = 0;
+ if ((gk > 0)&&(sr_advance[gk-1]))
+ sr_advance[gk] = 1;
+ if ((!M_AXI_WVALID || M_AXI_WREADY)
+ && cid_valid && current_id == sr_id[gk])
+ sr_advance[gk] = 1;
+ if (!sr_valid[gk])
+ sr_advance[gk] = 0;
+ end
+ // }}}
+
+ // sw_write
+ // {{{
+ // Do we write new data into this station?
+ always @(*)
+ begin
+ sr_write[gk] = S_AXI3_WVALID && S_AXI3_WREADY;
+ if (sr_valid[gk] && (!sr_advance[gk]
+ ||(gk < NSREG-1 && sr_valid[gk+1])))
+ sr_write[gk] = 0;
+ if (gk > 0 && (!sr_valid[gk-1]
+ || (sr_advance[gk-1] && !sr_valid[gk])))
+ sr_write[gk] = 0;
+ end
+ // }}}
+
+ // sr_valid
+ // {{{
+ initial sr_valid[gk] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ sr_valid[gk] <= 0;
+ else if (sr_write[gk])
+ sr_valid[gk] <= 1;
+ else if (sr_advance[gk] && gk<NSREG-1)
+ sr_valid[gk] <= sr_valid[gk+1];
+ else if (sr_advance[gk])
+ sr_valid[gk] <= 0;
+ // }}}
+
+ // sr_id, sr_data, sr_strb, sr_last
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ sr_id[gk] <= 0;
+ sr_data[gk] <= 0;
+ sr_strb[gk] <= 0;
+ sr_last[gk] <= 0;
+ end else if (sr_write[gk])
+ begin
+ sr_id[gk] <= S_AXI3_WID;
+ sr_data[gk] <= S_AXI3_WDATA;
+ sr_strb[gk] <= S_AXI3_WSTRB;
+ sr_last[gk] <= S_AXI3_WLAST;
+ end else if ((gk < NSREG-1) && sr_advance[gk])
+ begin
+ sr_id[gk] <= sr_id[gk+1];
+ sr_data[gk] <= sr_data[gk+1];
+ sr_strb[gk] <= sr_strb[gk+1];
+ sr_last[gk] <= sr_last[gk+1];
+
+ if (OPT_LOWPOWER && gk < NSREG-1 && !sr_valid[gk+1])
+ begin
+ // If we are running in low power,
+ // Keep every unused register == 0
+ sr_id[gk] <= 0;
+ sr_data[gk] <= 0;
+ sr_strb[gk] <= 0;
+ sr_last[gk] <= 0;
+ end
+ end else if ((!OPT_LOWPOWER && !sr_valid[gk])
+ || sr_write[gk])
+ begin
+ sr_id[gk] <= S_AXI3_WID;
+ sr_data[gk] <= S_AXI3_WDATA;
+ sr_strb[gk] <= S_AXI3_WSTRB;
+ sr_last[gk] <= S_AXI3_WLAST;
+ end
+ // }}}
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Pick a register location to output
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ reg known_next;
+ reg [LGWFIFO-1:0] sr_next;
+ always @(*)
+ begin
+ known_next = 0;
+ sr_next = -1;
+ for(ik=0; ik<NSREG; ik=ik+1)
+ if (!known_next && current_id == sr_id[ik])
+ begin
+ sr_next = ik[LGWFIFO-1:0];
+ known_next = sr_valid[ik];
+ end
+
+ if (!cid_valid)
+ known_next = 0;
+ end
+
+ assign S_AXI3_WREADY = !sr_valid[NSREG-1];
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ assign read_awid_fifo = (!M_AXI_WVALID || M_AXI_WREADY)
+ && (!cid_valid
+ || (known_next && sr_last[sr_next]));
+
+ always @(*)
+ read_beat_fifo = (!M_AXI_WVALID || M_AXI_WREADY)
+ &&(known_next);
+
+ initial M_AXI_WVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_WVALID <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ M_AXI_WVALID <= known_next;
+
+ initial M_AXI_WID = 0;
+ initial M_AXI_WDATA = 0;
+ initial M_AXI_WSTRB = 0;
+ initial M_AXI_WLAST = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ // {{{
+ M_AXI_WID <= 0;
+ M_AXI_WDATA <= 0;
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= 0;
+ // }}}
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (OPT_LOWPOWER && !known_next)
+ begin
+ // {{{
+ M_AXI_WID <= 0;
+ M_AXI_WDATA <= 0;
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= 0;
+ // }}}
+ end else begin
+ // {{{
+ M_AXI_WID <= current_id;
+ // Verilator lint_off WIDTH
+ M_AXI_WDATA <= sr_data[sr_next];
+ M_AXI_WSTRB <= sr_strb[sr_next];
+ M_AXI_WLAST <= sr_last[sr_next];
+ // Verilator lint_on WIDTH
+ // }}}
+ end
+ end
+ // }}}
+ // Verilator lint_off UNUSED
+ wire unused_sreg;
+ assign unused_sreg = &{ 1'b0, read_beat_fifo };
+ // Verilator lint_on UNUSED
+`ifdef FORMAL
+ always @(*)
+ if (S_AXI3_WVALID && S_AXI3_WREADY)
+ begin
+ assert($onehot(sr_write));
+ end else if (S_AXI_ARESETN)
+ assert(sr_write == 0);
+
+ always @(*)
+ assert(((sr_write << 1) & sr_valid) == 0);
+
+ reg cvr_sreg_full;
+
+ initial cvr_sreg_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_sreg_full <= 0;
+ else if (!S_AXI3_WREADY)
+ cvr_sreg_full <= 1;
+
+ always @(*)
+ cover(cvr_sreg_full && sr_valid == 0);
+
+`endif
+ // }}}
+ end else if (OPT_METHOD == MTHD_PERID_FIFOS)
+ begin : MULTIPLE_FIFOS
+ // {{{
+ wire [NUM_FIFOS-1:0] wbfifo_full, wbfifo_empty, wbfifo_last;
+ // wire [IW-1:0] wbfifo_id [0:NUM_FIFOS-1];
+ wire [DW-1:0] wbfifo_data [0:NUM_FIFOS-1];
+ wire [DW/8-1:0] wbfifo_strb [0:NUM_FIFOS-1];
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // The write beat FIFO
+ ////////////////////////////////////////////////////////////////
+ // {{{
+ //
+ // Every write beat goes into a separate FIFO based on its ID
+ //
+ for (gk=0; gk < NUM_FIFOS; gk=gk+1)
+ begin
+ // {{{
+ wire [LGWFIFO:0] wbfifo_fill;
+
+ sfifo #(
+ .BW(DW+(DW/8)+1),
+ .LGFLEN(LGWFIFO),
+ .OPT_READ_ON_EMPTY(OPT_LOW_LATENCY)
+ ) write_beat_fifo (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(S_AXI3_WVALID && S_AXI3_WREADY && S_AXI3_WID == gk),
+ .i_data({ S_AXI3_WDATA, S_AXI3_WSTRB,
+ S_AXI3_WLAST }),
+ .o_full(wbfifo_full[gk]), .o_fill(wbfifo_fill),
+ .i_rd(read_beat_fifo && current_id == gk),
+ .o_data({ wbfifo_data[gk],
+ wbfifo_strb[gk], wbfifo_last[gk] }),
+ .o_empty(wbfifo_empty[gk])
+ );
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, wbfifo_fill };
+ // Verilator lint_on UNUSED
+ // }}}
+ end
+
+ assign S_AXI3_WREADY = !wbfifo_full[S_AXI3_WID];
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Outgoing write data channel
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ assign read_awid_fifo = (!M_AXI_WVALID || M_AXI_WREADY)
+ && (!cid_valid
+ || (read_beat_fifo && wbfifo_last[current_id]));
+ // Write packets associated with the current ID can move forward
+ always @(*)
+ read_beat_fifo = (!M_AXI_WVALID || M_AXI_WREADY)
+ &&(cid_valid)&&(!wbfifo_empty[current_id]);
+
+ initial M_AXI_WVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_WVALID <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ M_AXI_WVALID <= read_beat_fifo;
+
+ initial M_AXI_WID = 0;
+ initial M_AXI_WDATA = 0;
+ initial M_AXI_WSTRB = 0;
+ initial M_AXI_WLAST = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ // {{{
+ M_AXI_WID <= 0;
+ M_AXI_WDATA <= 0;
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= 0;
+ // }}}
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ // {{{
+ M_AXI_WID <= current_id;
+ M_AXI_WDATA <= wbfifo_data[current_id];
+ M_AXI_WSTRB <= wbfifo_strb[current_id];
+ M_AXI_WLAST <= wbfifo_last[current_id];
+
+ if (OPT_LOWPOWER && !read_beat_fifo)
+ begin
+ // {{{
+ M_AXI_WID <= 0;
+ M_AXI_WDATA <= 0;
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= 0;
+ // }}}
+ end
+ // }}}
+ end
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, awfifo_fill };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ reg [LGAWFIFO+LGWFIFO-1:0] f_awid_count;
+ (* anyconst *) reg [IW-1:0] f_const_id;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assume(!S_AXI3_AWVALID);
+ assume(!S_AXI3_WVALID);
+
+ assume(!M_AXI_WVALID);
+ end else begin
+ if ($past(S_AXI3_AWVALID && !S_AXI3_AWREADY))
+ begin
+ assume(S_AXI3_AWVALID);
+ assume($stable(S_AXI3_AWID));
+ end
+
+ if ($past(S_AXI3_WVALID && !S_AXI3_WREADY))
+ begin
+ assume(S_AXI3_WVALID);
+ assume($stable(S_AXI3_WID));
+ assume($stable(S_AXI3_WDATA));
+ assume($stable(S_AXI3_WSTRB));
+ assume($stable(S_AXI3_WLAST));
+ end
+
+ if ($past(M_AXI_WVALID && !M_AXI_WREADY))
+ begin
+ assert(M_AXI_WVALID);
+ assert($stable(M_AXI_WID));
+ assert($stable(M_AXI_WDATA));
+ assert($stable(M_AXI_WSTRB));
+ assert($stable(M_AXI_WLAST));
+ end
+ end
+
+ generate if (OPT_METHOD != MTHD_NONE)
+ begin : F_FIFO_CHECK
+ wire [LGWFIFO:0] f_ckfifo_fill;
+ wire [LGWFIFO:0] f_ckfifo_full, f_ckfifo_empty;
+ wire [IW-1:0] f_ckfifo_id;
+ wire [DW-1:0] f_ckfifo_data;
+ wire [DW/8-1:0] f_ckfifo_strb;
+ wire f_ckfifo_last;
+
+ sfifo #(
+ .BW(IW + DW + (DW/8) + 1),
+ .LGFLEN(LGWFIFO),
+ .OPT_READ_ON_EMPTY(OPT_LOW_LATENCY)
+ ) f_checkfifo (
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(S_AXI3_WVALID && S_AXI3_WREADY
+ && S_AXI3_WID == f_const_id),
+ .i_data({ S_AXI3_WID, S_AXI3_WDATA, S_AXI3_WSTRB, S_AXI3_WLAST }),
+ .o_full(f_ckfifo_full), .o_fill(f_ckfifo_fill),
+ .i_rd(M_AXI_WVALID && M_AXI_WREADY
+ && M_AXI_WID == f_const_id),
+ .o_data({ f_ckfifo_id, f_ckfifo_data, f_ckfifo_strb,
+ f_ckfifo_last }),
+ .o_empty(f_ckfifo_empty)
+ );
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_WVALID && M_AXI_WID == f_const_id)
+ begin
+ assert(!f_ckfifo_empty);
+ assert(f_ckfifo_id == M_AXI_WID);
+ assert(f_ckfifo_data == M_AXI_WDATA);
+ assert(f_ckfifo_strb == M_AXI_WSTRB);
+ assert(f_ckfifo_last == M_AXI_WLAST);
+ end
+ end endgenerate
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_awid_count = 0;
+ else case({S_AXI3_AWVALID&& S_AXI3_AWREADY && S_AXI3_AWID == f_const_id,
+ M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST
+ && M_AXI_WID == f_const_id })
+ 2'b01: f_awid_count <= f_awid_count - 1;
+ 2'b10: f_awid_count <= f_awid_count + 1;
+ default begin end
+ endcase
+
+ always @(*)
+ if (M_AXI_WVALID && M_AXI_WID == f_const_id)
+ assert(f_awid_count > 0);
+
+ generate if (OPT_LOWPOWER)
+ begin : F_LOWPOWER_CHECK
+ always @(*)
+ if (!S_AXI3_AWVALID)
+ assume(S_AXI3_AWID == 0);
+
+ always @(*)
+ if (!S_AXI3_WVALID)
+ begin
+ assume(S_AXI3_WID == 0);
+ assume(S_AXI3_WDATA == 0);
+ assume(S_AXI3_WSTRB == 0);
+ assume(S_AXI3_WLAST == 0);
+ end
+
+ always @(*)
+ if (!M_AXI_WVALID)
+ begin
+ assert(M_AXI_WID == 0);
+ assert(M_AXI_WDATA == 0);
+ assert(M_AXI_WSTRB == 0);
+ assert(M_AXI_WLAST == 0);
+ end
+
+ end endgenerate
+
+`endif
+ // }}}
+endmodule
diff --git a/rtl/wb2axip/axi_addr.v b/rtl/wb2axip/axi_addr.v
new file mode 100644
index 0000000..8d8ac75
--- /dev/null
+++ b/rtl/wb2axip/axi_addr.v
@@ -0,0 +1,235 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axi_addr.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: The AXI (full) standard has some rather complicated addressing
+// modes, where the address can either be FIXED, INCRementing, or
+// even where it can WRAP around some boundary. When in either INCR or
+// WRAP modes, the next address must always be aligned. In WRAP mode,
+// the next address calculation needs to wrap around a given value, and
+// that value is dependent upon the burst size (i.e. bytes per beat) and
+// length (total numbers of beats). Since this calculation can be
+// non-trivial, and since it needs to be done multiple times, the logic
+// below captures it for every time it might be needed.
+//
+// 20200918 - modified to accommodate (potential) AXI3 burst lengths
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axi_addr #(
+ // {{{
+ parameter AW = 32,
+ DW = 32,
+ // parameter [0:0] OPT_AXI3 = 1'b0,
+ localparam LENB = 8
+ // }}}
+ ) (
+ // {{{
+ input wire [AW-1:0] i_last_addr,
+ input wire [2:0] i_size, // 1b, 2b, 4b, 8b, etc
+ input wire [1:0] i_burst, // fixed, incr, wrap, reserved
+ input wire [LENB-1:0] i_len,
+ output wire [AW-1:0] o_next_addr
+ // }}}
+ );
+
+ // Parameter/register declarations
+ // {{{
+ localparam DSZ = $clog2(DW)-3;
+ localparam [1:0] FIXED = 2'b00;
+ // localparam [1:0] INCREMENT = 2'b01;
+ // localparam [1:0] WRAP = 2'b10;
+ localparam IN_AW = (AW >= 12) ? 12 : AW;
+ localparam [IN_AW-1:0] ONE = 1;
+
+ reg [IN_AW-1:0] wrap_mask, increment;
+ reg [IN_AW-1:0] crossblk_addr, aligned_addr, unaligned_addr;
+ // }}}
+
+ // Address increment
+ // {{{
+ always @(*)
+ if (DSZ == 0)
+ increment = 1;
+ else if (DSZ == 1)
+ increment = (i_size[0]) ? 2 : 1;
+ else if (DSZ == 2)
+ increment = (i_size[1]) ? 4 : ((i_size[0]) ? 2 : 1);
+ else if (DSZ == 3)
+ case(i_size[1:0])
+ 2'b00: increment = 1;
+ 2'b01: increment = 2;
+ 2'b10: increment = 4;
+ 2'b11: increment = 8;
+ endcase
+ else
+ increment = (1<<i_size);
+ // }}}
+
+ // wrap_mask
+ // {{{
+ // The wrap_mask is used to determine which bits remain stable across
+ // the burst, and which are allowed to change. It is only used during
+ // wrapped addressing.
+ always @(*)
+ begin
+ // Start with the default, minimum mask
+
+ /*
+ // Here's the original code. It works, but it's
+ // not economical (uses too many LUTs)
+ //
+ if (i_len[3:0] == 1)
+ wrap_mask = (1<<(i_size+1));
+ else if (i_len[3:0] == 3)
+ wrap_mask = (1<<(i_size+2));
+ else if (i_len[3:0] == 7)
+ wrap_mask = (1<<(i_size+3));
+ else if (i_len[3:0] == 15)
+ wrap_mask = (1<<(i_size+4));
+ wrap_mask = wrap_mask - 1;
+ */
+
+ // Here's what we *want*
+ //
+ // wrap_mask[i_size:0] = -1;
+ //
+ // On the other hand, since we're already guaranteed that our
+ // addresses are aligned, do we really care about
+ // wrap_mask[i_size-1:0] ?
+
+ // What we want:
+ //
+ // wrap_mask[i_size+3:i_size] |= i_len[3:0]
+ //
+ // We could simplify this to
+ //
+ // wrap_mask = wrap_mask | (i_len[3:0] << (i_size));
+ // Verilator lint_off WIDTH
+ if (DSZ < 2)
+ wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[0]));
+ else if (DSZ < 4)
+ wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[1:0]));
+ else
+ wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size));
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // unaligned_addr
+ always @(*)
+ unaligned_addr = i_last_addr[IN_AW-1:0] + increment[IN_AW-1:0];
+
+ // aligned_addr
+ // {{{
+ always @(*)
+ if (i_burst != FIXED)
+ begin
+ // Align subsequent beats in any burst
+ // {{{
+ aligned_addr = unaligned_addr;
+ // We use the bus size here to simplify the logic
+ // required in case the bus is smaller than the
+ // maximum. This depends upon AxSIZE being less than
+ // $clog2(DATA_WIDTH/8).
+ if (DSZ < 2)
+ begin
+ // {{{
+ // Align any subsequent address
+ if (i_size[0])
+ aligned_addr[0] = 0;
+ // }}}
+ end else if (DSZ < 4)
+ begin
+ // {{{
+ // Align any subsequent address
+ case(i_size[1:0])
+ 2'b00: aligned_addr = unaligned_addr;
+ 2'b01: aligned_addr[ 0] = 0;
+ 2'b10: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]= 0;
+ 2'b11: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]= 0;
+ endcase
+ // }}}
+ end else begin
+ // {{{
+ // Align any subsequent address
+ case(i_size)
+ 3'b001: aligned_addr[ 0] = 0;
+ 3'b010: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]=0;
+ 3'b011: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]=0;
+ 3'b100: aligned_addr[(AW-1>3) ? 3 : (AW-1):0]=0;
+ 3'b101: aligned_addr[(AW-1>4) ? 4 : (AW-1):0]=0;
+ 3'b110: aligned_addr[(AW-1>5) ? 5 : (AW-1):0]=0;
+ 3'b111: aligned_addr[(AW-1>6) ? 6 : (AW-1):0]=0;
+ default: aligned_addr = unaligned_addr;
+ endcase
+ // }}}
+ end
+ // }}}
+ end else
+ aligned_addr = i_last_addr[IN_AW-1:0];
+ // }}}
+
+ // crossblk_addr from aligned_addr, for WRAP addressing
+ // {{{
+ always @(*)
+ if (i_burst[1])
+ begin
+ // WRAP!
+ crossblk_addr[IN_AW-1:0] = (i_last_addr[IN_AW-1:0] & ~wrap_mask)
+ | (aligned_addr & wrap_mask);
+ end else
+ crossblk_addr[IN_AW-1:0] = aligned_addr;
+ // }}}
+
+ // o_next_addr: Guarantee only the bottom 12 bits change
+ // {{{
+ // This is really a logic simplification. AXI bursts aren't allowed
+ // to cross 4kB boundaries. Given that's the case, we don't have to
+ // suffer from the propagation across all AW bits, and can limit any
+ // address propagation to just the lower 12 bits
+ generate if (AW > 12)
+ begin : WIDE_ADDRESS
+ assign o_next_addr = { i_last_addr[AW-1:12],
+ crossblk_addr[11:0] };
+ end else begin : NARROW_ADDRESS
+ assign o_next_addr = crossblk_addr[AW-1:0];
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = (LENB <= 4) ? &{1'b0, i_len[0] }
+ : &{ 1'b0, i_len[LENB-1:4], i_len[0] };
+ // Verilator lint_on UNUSED
+ // }}}
+endmodule
diff --git a/rtl/wb2axip/axidma.v b/rtl/wb2axip/axidma.v
new file mode 100644
index 0000000..46b1e69
--- /dev/null
+++ b/rtl/wb2axip/axidma.v
@@ -0,0 +1,2977 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axidma.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: To move memory from one location to another, at high speed.
+// This is not a memory to stream, nor a stream to memory core,
+// but rather a memory to memory core.
+//
+//
+// Registers:
+//
+// 0. Control
+// 8b KEY
+// 3'b PROT
+// 4'b QOS
+// 1b Abort: Either aborting or aborted
+// 1b Err: Ended on an error
+// 1b Busy
+// 1b Interrupt Enable
+// 1b Interrupt Clear
+// 1b Start
+// 1. Unused
+// 2-3. Source address, low and then high 64-bit words
+// (Last value read address)
+// 4-5. Destination address, low and then high 64-bit words
+// (Next value to write address)
+// 6-7. Length, low and then high words
+// (Total number minus successful writes)
+//
+// Performance goals:
+// 100% throughput
+// Stay off the bus until you can drive it hard
+// Other goals:
+// Be both AXI3 and AXI4 capable
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// `define AXI3
+// }}}
+module axidma #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ //
+ // These two "parameters" really aren't things that can be
+ // changed externally. They control the size of the AXI4-lite
+ // port. Internally, it's defined to have 8, 32-bit registers.
+ // The registers are configured wide enough to support 64-bit
+ // AXI addressing. Similarly, the AXI-lite data width is fixed
+ // at 32-bits.
+ localparam C_AXIL_ADDR_WIDTH = 5,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ //
+ // OPT_UNALIGNED turns on support for unaligned addresses,
+ // whether source, destination, or length parameters.
+ parameter [0:0] OPT_UNALIGNED = 1'b1,
+ //
+ // OPT_WRAPMEM controls what happens if the transfer runs off
+ // of the end of memory. If set, the transfer will continue
+ // again from the beginning of memory. If clear, the transfer
+ // will be aborted with an error if either read or write
+ // address ever get this far.
+ parameter [0:0] OPT_WRAPMEM = 1'b1,
+ //
+ // LGMAXBURST controls the size of the maximum burst produced
+ // by this core. Specifically, its the log (based 2) of that
+ // maximum size. Hence, for AXI4, this size must be 8
+ // (i.e. 2^8 or 256 beats) or less. For AXI3, the size must
+ // be 4 or less. Tests have verified performance for
+ // LGMAXBURST as low as 2. While I expect it to fail at
+ // LGMAXBURST=0, I haven't verified at what value this burst
+ // parameter is too small.
+`ifdef AXI3
+ parameter LGMAXBURST=4, // 16 beats max
+`else
+ parameter LGMAXBURST=8, // 256 beats
+`endif
+ // LGFIFO: This is the (log-based-2) size of the internal FIFO.
+ // Hence if LGFIFO=8, the internal FIFO will have 256 elements
+ // (words) in it. High throughput transfers are accomplished
+ // by first storing data into a FIFO, then once a full burst
+ // size is available bursting that data over the bus. In
+ // order to be able to keep receiving data while bursting it
+ // out, the FIFO size must be at least twice the size of the
+ // maximum burst size. Larger sizes are possible as well.
+ parameter LGFIFO = LGMAXBURST+1, // 512 element FIFO
+ //
+ // LGLEN: specifies the number of bits in the transfer length
+ // register. If a transfer cannot be specified in LGLEN bits,
+ // it won't happen. LGLEN must be less than or equal to the
+ // address width.
+ parameter LGLEN = C_AXI_ADDR_WIDTH,
+ //
+ // OPT_LOWPOWER:
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ //
+ // OPT_CLKGATE:
+ parameter [0:0] OPT_CLKGATE = OPT_LOWPOWER,
+ //
+ // AXI uses ID's to transfer information. This core rather
+ // ignores them. Instead, it uses a constant ID for all
+ // transfers. The following two parameters control that ID.
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_READ_ID = 0,
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_WRITE_ID = 0,
+ //
+ // The "ABORT_KEY" is a byte that, if written to the control
+ // word while the core is running, will cause the data transfer
+ // to be aborted.
+ parameter [7:0] ABORT_KEY = 8'h6d,
+ //
+ localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AXILLSB= $clog2(C_AXIL_DATA_WIDTH)-3,
+ localparam LGLENW= LGLEN-ADDRLSB
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI low-power interface
+ // {{{
+ // This has been removed, due to a lack of definition from the
+ // AXI standard for these wires.
+ // }}}
+ //
+ // The AXI4-lite control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output reg S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output reg S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output reg [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ // The AXI Master (DMA) interface
+ // {{{
+ output reg M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output reg [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output reg [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+`ifdef AXI3
+ output reg [3:0] M_AXI_AWLEN,
+`else
+ output reg [7:0] M_AXI_AWLEN,
+`endif
+ output reg [2:0] M_AXI_AWSIZE,
+ output reg [1:0] M_AXI_AWBURST,
+ output reg M_AXI_AWLOCK,
+ output reg [3:0] M_AXI_AWCACHE,
+ output reg [2:0] M_AXI_AWPROT,
+ output reg [3:0] M_AXI_AWQOS,
+ //
+ //
+ output reg M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+`ifdef AXI3
+ output reg [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+`endif
+ output reg [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output reg [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output reg M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output reg M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ //
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output reg [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+`ifdef AXI3
+ output reg [3:0] M_AXI_ARLEN,
+`else
+ output reg [7:0] M_AXI_ARLEN,
+`endif
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP,
+ // }}}
+ output reg o_int
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ // The number of beats in this maximum burst size is
+ // automatically determined from LGMAXBURST, and so its
+ // forced to be a power of two this way.
+ localparam MAXBURST=(1<<LGMAXBURST);
+ //
+ localparam [2:0] CTRL_ADDR = 3'b000,
+ // UNUSED_ADDR = 3'b001,
+ SRCLO_ADDR = 3'b010,
+ SRCHI_ADDR = 3'b011,
+ DSTLO_ADDR = 3'b100,
+ DSTHI_ADDR = 3'b101,
+ LENLO_ADDR = 3'b110,
+ LENHI_ADDR = 3'b111;
+ localparam CTRL_START_BIT = 0,
+ CTRL_BUSY_BIT = 0,
+ CTRL_INT_BIT = 1,
+ CTRL_INTEN_BIT = 2,
+ CTRL_ABORT_BIT = 3,
+ CTRL_ERR_BIT = 4;
+ localparam [1:0] AXI_INCR = 2'b01, AXI_OKAY = 2'b00;
+
+ wire gated_clk, clk_active;
+ wire i_clk = gated_clk;
+ wire i_reset = !S_AXI_ARESETN;
+
+ reg axil_write_ready, axil_read_ready;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_src, wide_dst, wide_len;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] new_widesrc, new_widedst, new_widelen;
+
+ reg r_busy, r_err, r_abort, w_start, r_int, r_int_enable,
+ r_done, last_write_ack, zero_len;
+ reg [3:0] r_qos;
+ reg [2:0] r_prot;
+ reg [LGLEN-1:0] r_len; // Length of transfer in octets
+ reg [C_AXI_ADDR_WIDTH-1:0] r_src_addr, r_dst_addr;
+
+ reg fifo_reset;
+ wire [LGFIFO:0] fifo_fill;
+ reg [LGFIFO:0] fifo_space_available;
+ reg [LGFIFO:0] fifo_data_available,
+ next_fifo_data_available;
+ wire fifo_full, fifo_empty;
+ reg [8:0] write_count;
+ //
+ reg phantom_read, w_start_read,
+ no_read_bursts_outstanding;
+ reg [LGLEN:0] readlen_b;
+ reg [LGLENW:0] readlen_w, initial_readlen_w;
+ reg [C_AXI_ADDR_WIDTH:0] read_address;
+ reg [LGLENW:0] reads_remaining_w,
+ read_beats_remaining_w,
+ read_bursts_outstanding;
+ reg [C_AXI_ADDR_WIDTH-1:0] read_distance_to_boundary_b;
+ reg reads_remaining_nonzero;
+ //
+ reg phantom_write, w_write_start;
+ reg [C_AXI_ADDR_WIDTH:0] write_address;
+ reg [LGLENW:0] writes_remaining_w,
+ write_bursts_outstanding;
+ reg [LGLENW:0] write_burst_length;
+ reg write_requests_remaining;
+ reg [LGLEN:0] writelen_b;
+ reg [LGLENW:0] write_beats_remaining;
+
+ wire awskd_valid;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+
+ wire arskd_valid;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ //
+ reg r_partial_in_valid;
+ reg r_write_fifo, r_read_fifo;
+ reg r_partial_outvalid;
+ reg [C_AXI_DATA_WIDTH/8-1:0] r_first_wstrb,
+ r_last_wstrb;
+ reg extra_realignment_write,
+ extra_realignment_read;
+ reg [2*ADDRLSB+2:0] write_realignment;
+ reg last_read_beat;
+ reg clear_read_pipeline;
+ reg last_write_burst;
+
+ //
+ // Push some write length calculations across clocks
+ reg [LGLENW:0] w_writes_remaining_w;
+ reg multiple_write_bursts_remaining,
+ first_write_burst;
+ reg [LGMAXBURST:0] initial_write_distance_to_boundary_w,
+ first_write_len_w;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-Lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite control write interface
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WSTRB, S_AXIL_WDATA }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_strb, wskd_data }));
+
+ always @(*)
+ begin
+ axil_write_ready = !S_AXIL_BVALID || S_AXIL_BREADY;
+ if (!awskd_valid || !wskd_valid)
+ axil_write_ready = 0;
+ if (!clk_active)
+ axil_write_ready = 0;
+ end
+
+ initial S_AXIL_BVALID = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ S_AXIL_BVALID <= 1'b0;
+ else if (!S_AXIL_BVALID || S_AXIL_BREADY)
+ S_AXIL_BVALID <= axil_write_ready;
+
+ assign S_AXIL_BRESP = AXI_OKAY;
+
+ always @(*)
+ begin
+ w_start = !r_busy && axil_write_ready && wskd_strb[0]
+ && wskd_data[CTRL_START_BIT]
+ && (awskd_addr == CTRL_ADDR);
+ if (r_err && (!wskd_strb[0] || !wskd_data[CTRL_ERR_BIT]))
+ w_start = 0;
+ if (zero_len)
+ w_start = 0;
+ end
+
+ always @(posedge i_clk)
+ if (i_reset)
+ r_err <= 1'b0;
+ else if (!r_busy && axil_write_ready)
+ r_err <= (r_err) && (!wskd_strb[0] || !wskd_data[CTRL_ERR_BIT]);
+ else if (r_busy)
+ begin
+ if (M_AXI_BVALID && M_AXI_BRESP[1])
+ r_err <= 1'b1;
+ if (M_AXI_RVALID && M_AXI_RRESP[1])
+ r_err <= 1'b1;
+
+ if (!OPT_WRAPMEM && write_address[C_AXI_ADDR_WIDTH]
+ && write_requests_remaining)
+ r_err <= 1'b1;
+ if (!OPT_WRAPMEM && read_address[C_AXI_ADDR_WIDTH]
+ && reads_remaining_nonzero)
+ r_err <= 1'b1;
+ end
+
+ initial r_busy = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_busy <= 1'b0;
+ else if (!r_busy && axil_write_ready)
+ r_busy <= w_start;
+ else if (r_busy)
+ begin
+ if (M_AXI_BVALID && M_AXI_BREADY && last_write_ack)
+ r_busy <= 1'b0;
+ if (r_done)
+ r_busy <= 1'b0;
+ end
+
+ always @(posedge i_clk)
+ if (i_reset || !r_int_enable || !r_busy)
+ o_int <= 0;
+ else if (r_done)
+ o_int <= 1'b1;
+ else
+ o_int <= (last_write_ack && M_AXI_BVALID && M_AXI_BREADY);
+
+ always @(posedge i_clk)
+ if (i_reset)
+ r_int <= 0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready && awskd_addr == CTRL_ADDR
+ && wskd_strb[0])
+ begin
+ if (wskd_data[CTRL_START_BIT])
+ r_int <= 0;
+ else if (wskd_data[CTRL_INT_BIT])
+ r_int <= 0;
+ end
+ end else if (r_done)
+ r_int <= 1'b1;
+ else
+ r_int <= (last_write_ack && M_AXI_BVALID && M_AXI_BREADY);
+
+ initial r_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_abort <= 1'b0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready && awskd_addr == CTRL_ADDR
+ && wskd_strb[0])
+ begin
+ if(wskd_data[CTRL_START_BIT]
+ ||wskd_data[CTRL_ABORT_BIT]
+ ||wskd_data[CTRL_ERR_BIT])
+ r_abort <= 0;
+ end
+ end else if (!r_abort)
+ r_abort <= (axil_write_ready && awskd_addr == CTRL_ADDR)
+ &&(wskd_strb[3] && wskd_data[31:24] == ABORT_KEY);
+
+ wire [C_AXIL_DATA_WIDTH-1:0] newsrclo, newsrchi,
+ newdstlo, newdsthi, newlenlo, newlenhi;
+
+ always @(*)
+ begin
+ wide_src = 0;
+ wide_dst = 0;
+ wide_len = 0;
+
+ wide_src[C_AXI_ADDR_WIDTH-1:0] = r_src_addr;
+ wide_dst[C_AXI_ADDR_WIDTH-1:0] = r_dst_addr;
+ wide_len[LGLEN-1:0] = r_len;
+
+ if (!OPT_UNALIGNED)
+ begin
+ wide_src[ADDRLSB-1:0] = 0;
+ wide_dst[ADDRLSB-1:0] = 0;
+ wide_len[ADDRLSB-1:0] = 0;
+ end
+ end
+
+ assign newsrclo = apply_wstrb(
+ wide_src[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign newsrchi = apply_wstrb(
+ wide_src[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ assign newdstlo = apply_wstrb(
+ wide_dst[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign newdsthi = apply_wstrb(
+ wide_dst[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ assign newlenlo = apply_wstrb(
+ wide_len[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign newlenhi = apply_wstrb(
+ wide_len[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+
+ always @(*)
+ begin
+ new_widesrc = wide_src;
+ new_widedst = wide_dst;
+ new_widelen = wide_len;
+
+ if (!awskd_addr[0])
+ begin
+ new_widesrc[C_AXIL_DATA_WIDTH-1:0] = newsrclo;
+ new_widedst[C_AXIL_DATA_WIDTH-1:0] = newdstlo;
+ new_widelen[C_AXIL_DATA_WIDTH-1:0] = newlenlo;
+ end else begin
+ new_widesrc[2*C_AXIL_DATA_WIDTH-1
+ :C_AXIL_DATA_WIDTH] = newsrchi;
+ new_widedst[2*C_AXIL_DATA_WIDTH-1
+ :C_AXIL_DATA_WIDTH] = newdsthi;
+ new_widelen[2*C_AXIL_DATA_WIDTH-1
+ :C_AXIL_DATA_WIDTH] = newlenhi;
+ end
+
+ new_widesrc[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+ new_widedst[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:LGLEN] = 0;
+
+ if (!OPT_UNALIGNED)
+ begin
+ new_widesrc[ADDRLSB-1:0] = 0;
+ new_widedst[ADDRLSB-1:0] = 0;
+ new_widelen[ADDRLSB-1:0] = 0;
+ end
+ end
+
+ initial r_len = 0;
+ initial zero_len = 1;
+ initial r_src_addr = 0;
+ initial r_dst_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ // {{{
+ r_len <= 0;
+ zero_len <= 1;
+ r_prot <= 0;
+ r_qos <= 0;
+ r_src_addr <= 0;
+ r_dst_addr <= 0;
+ r_int_enable <= 0;
+ // }}}
+ end else if (!r_busy && axil_write_ready)
+ begin
+ // {{{
+ case(awskd_addr)
+ CTRL_ADDR: begin
+ if (wskd_strb[2])
+ begin
+ r_prot <= wskd_data[22:20];
+ r_qos <= wskd_data[19:16];
+ end
+ if (wskd_strb[0])
+ r_int_enable <= wskd_data[CTRL_INTEN_BIT];
+ end
+ SRCLO_ADDR: begin
+ r_src_addr <= new_widesrc[C_AXI_ADDR_WIDTH-1:0];
+ end
+ SRCHI_ADDR: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH) begin
+ r_src_addr <= new_widesrc[C_AXI_ADDR_WIDTH-1:0];
+ end
+ DSTLO_ADDR: begin
+ r_dst_addr <= new_widedst[C_AXI_ADDR_WIDTH-1:0];
+ end
+ DSTHI_ADDR: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH) begin
+ r_dst_addr <= new_widedst[C_AXI_ADDR_WIDTH-1:0];
+ end
+ LENLO_ADDR: begin
+ r_len <= new_widelen[LGLEN-1:0];
+ zero_len <= (new_widelen == 0);
+ end
+ LENHI_ADDR: if (LGLEN > C_AXIL_DATA_WIDTH) begin
+ r_len <= new_widelen[LGLEN-1:0];
+ zero_len <= (new_widelen == 0);
+ end
+ default: begin end
+ endcase
+ // }}}
+ end else if (r_busy)
+ begin
+ // {{{
+ r_dst_addr <= write_address[C_AXI_ADDR_WIDTH-1:0];
+ if (writes_remaining_w[LGLENW])
+ r_len <= -1;
+ else
+ r_len <= { writes_remaining_w[LGLENW-1:0],
+ {(ADDRLSB){1'b0}} };
+ if (OPT_UNALIGNED)
+ r_len[ADDRLSB-1:0] <= 0;
+
+ zero_len <= (writes_remaining_w == 0);
+
+ if (M_AXI_RVALID && M_AXI_RREADY && !M_AXI_RRESP[1])
+ begin
+ r_src_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ <= r_src_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]+1;
+ r_src_addr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+ end
+
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8] = wstrb[k] ? new_data[k*8 +: 8]
+ : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite control read interface
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ always @(*)
+ begin
+ axil_read_ready = !S_AXIL_RVALID || S_AXIL_RREADY;
+ if (!arskd_valid)
+ axil_read_ready = 1'b0;
+ if (!clk_active)
+ axil_read_ready = 1'b0;
+ end
+
+ initial S_AXIL_RVALID = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ S_AXIL_RVALID <= 1'b0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ S_AXIL_RVALID <= axil_read_ready;
+
+ always @(posedge i_clk)
+ if (i_reset)
+ S_AXIL_RDATA <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ begin
+ S_AXIL_RDATA <= 0;
+ case(arskd_addr)
+ CTRL_ADDR: begin
+ S_AXIL_RDATA[CTRL_ERR_BIT] <= r_err;
+ S_AXIL_RDATA[CTRL_ABORT_BIT] <= r_abort;
+ S_AXIL_RDATA[CTRL_INTEN_BIT] <= r_int_enable;
+ S_AXIL_RDATA[CTRL_INT_BIT] <= r_int;
+ S_AXIL_RDATA[CTRL_BUSY_BIT] <= r_busy;
+ end
+ SRCLO_ADDR:
+ S_AXIL_RDATA <= wide_src[C_AXIL_DATA_WIDTH-1:0];
+ SRCHI_ADDR:
+ S_AXIL_RDATA <= wide_src[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ DSTLO_ADDR:
+ S_AXIL_RDATA <= wide_dst[C_AXIL_DATA_WIDTH-1:0];
+ DSTHI_ADDR:
+ S_AXIL_RDATA <= wide_dst[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ LENLO_ADDR:
+ S_AXIL_RDATA <= wide_len[C_AXIL_DATA_WIDTH-1:0];
+ LENHI_ADDR:
+ S_AXIL_RDATA <= wide_len[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ default: begin end
+ endcase
+
+ if (!axil_read_ready)
+ S_AXIL_RDATA <= 0;
+ end
+
+ assign S_AXIL_RRESP = AXI_OKAY;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI read processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Read data into our FIFO
+ //
+
+ // read_address
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ read_address <= { 1'b0, r_src_addr };
+ else if (phantom_read)
+ begin
+ // Verilator lint_off WIDTH
+ read_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ <= read_address[C_AXI_ADDR_WIDTH:ADDRLSB] +(M_AXI_ARLEN+1);
+ // Verilator lint_on WIDTH
+ read_address[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // reads_remaining_w, reads_remaining_nonzero
+ // {{{
+ // Verilator lint_off WIDTH
+ always @(posedge i_clk)
+ if (!r_busy)
+ reads_remaining_w <= readlen_b[LGLEN:ADDRLSB];
+ else if (phantom_read)
+ reads_remaining_w <= reads_remaining_w - (M_AXI_ARLEN+1);
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ reads_remaining_nonzero <= 1;
+ else if (phantom_read)
+ reads_remaining_nonzero
+ <= (reads_remaining_w != (M_AXI_ARLEN+1));
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // read_beats_remaining_w
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ read_beats_remaining_w <= readlen_b[LGLEN:ADDRLSB];
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ read_beats_remaining_w <= read_beats_remaining_w - 1;
+ // }}}
+
+ // read_bursts_outstanding, no_read_bursts_outstanding
+ // {{{
+ initial read_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ read_bursts_outstanding <= 0;
+ end else case({phantom_read,M_AXI_RVALID&& M_AXI_RREADY && M_AXI_RLAST})
+ 2'b01: read_bursts_outstanding <= read_bursts_outstanding - 1;
+ 2'b10: read_bursts_outstanding <= read_bursts_outstanding + 1;
+ default: begin end
+ endcase
+
+ initial no_read_bursts_outstanding = 1;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ no_read_bursts_outstanding <= 1;
+ end else case({phantom_read,M_AXI_RVALID&& M_AXI_RREADY && M_AXI_RLAST})
+ 2'b01: no_read_bursts_outstanding <= (read_bursts_outstanding == 1);
+ 2'b10: no_read_bursts_outstanding <= 0;
+ default: begin end
+ endcase
+ // }}}
+
+ // M_AXI_ARADDR
+ // {{{
+ always @(posedge i_clk)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ M_AXI_ARADDR <= 0;
+ else if (!r_busy)
+ begin
+ if (!OPT_LOWPOWER || w_start)
+ M_AXI_ARADDR <= r_src_addr;
+ else
+ M_AXI_ARADDR <= 0;
+ end else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ M_AXI_ARADDR <= read_address[C_AXI_ADDR_WIDTH-1:0];
+ if (OPT_LOWPOWER && !w_start_read)
+ M_AXI_ARADDR <= 0;
+ end
+ // }}}
+
+ // readlen_b
+ // {{{
+ always @(*)
+ if (OPT_UNALIGNED)
+ readlen_b = r_len + { {(C_AXI_ADDR_WIDTH-ADDRLSB){1'b0}},
+ r_src_addr[ADDRLSB-1:0] }
+ + { {(C_AXI_ADDR_WIDTH-ADDRLSB){1'b0}},
+ {(ADDRLSB){1'b1}} };
+ else begin
+ readlen_b = { 1'b0, r_len };
+ readlen_b[ADDRLSB-1:0] = 0;
+ end
+ // }}}
+
+ // read_distance_to_boundary_b
+ // {{{
+ always @(*)
+ begin
+ read_distance_to_boundary_b = 0;
+ read_distance_to_boundary_b[ADDRLSB +: LGMAXBURST]
+ = -r_src_addr[ADDRLSB +: LGMAXBURST];
+ end
+ // }}}
+
+ // initial_readlen_w
+ // {{{
+ always @(*)
+ begin
+ initial_readlen_w = 0;
+ initial_readlen_w[LGMAXBURST] = 1;
+
+ if (r_src_addr[ADDRLSB +: LGMAXBURST] != 0)
+ initial_readlen_w[LGMAXBURST:0] = { 1'b0,
+ read_distance_to_boundary_b[ADDRLSB +: LGMAXBURST] };
+ if (initial_readlen_w > readlen_b[LGLEN:ADDRLSB])
+ initial_readlen_w[LGMAXBURST:0] = { 1'b0,
+ readlen_b[ADDRLSB +: LGMAXBURST] };
+ initial_readlen_w[LGLENW-1:LGMAXBURST+1] = 0;
+ end
+ // }}}
+
+ // readlen_w
+ // {{{
+ // Verilator lint_off WIDTH
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ readlen_w <= initial_readlen_w;
+ end else if (phantom_read)
+ begin
+ readlen_w <= reads_remaining_w - (M_AXI_ARLEN+1);
+ if (reads_remaining_w - (M_AXI_ARLEN+1) > MAXBURST)
+ readlen_w <= MAXBURST;
+ end
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // w_start_read
+ // {{{
+ always @(*)
+ begin
+ w_start_read = r_busy && reads_remaining_nonzero;
+ if (phantom_read)
+ w_start_read = 0;
+ if (!OPT_WRAPMEM && read_address[C_AXI_ADDR_WIDTH])
+ w_start_read = 0;
+ if (fifo_space_available < MAXBURST)
+ w_start_read = 0;
+ if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ w_start_read = 0;
+ if (r_err || r_abort)
+ w_start_read = 0;
+ end
+ // }}}
+
+ // M_AXI_ARVALID, phantom_read
+ // {{{
+ initial M_AXI_ARVALID = 1'b0;
+ initial phantom_read = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ M_AXI_ARVALID <= 0;
+ phantom_read <= 0;
+ end else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ M_AXI_ARVALID <= w_start_read;
+ phantom_read <= w_start_read;
+ end else
+ phantom_read <= 0;
+ // }}}
+
+ // M_AXI_ARLEN
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ M_AXI_ARLEN <= 0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+`ifdef AXI3
+ M_AXI_ARLEN <= readlen_w[3:0] - 4'h1;
+`else
+ M_AXI_ARLEN <= readlen_w[7:0] - 8'h1;
+`endif
+ if (OPT_LOWPOWER && !w_start_read)
+ M_AXI_ARLEN <= 0;
+ end
+ // }}}
+
+ assign M_AXI_ARID = AXI_READ_ID;
+ assign M_AXI_ARBURST = AXI_INCR;
+ assign M_AXI_ARSIZE = ADDRLSB[2:0];
+ assign M_AXI_ARLOCK = 1'b0;
+ assign M_AXI_ARCACHE = 4'b0011;
+ assign M_AXI_ARPROT = r_prot;
+ assign M_AXI_ARQOS = r_qos;
+ //
+ assign M_AXI_RREADY = !no_read_bursts_outstanding;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The intermediate FIFO
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ fifo_reset = i_reset || !r_busy || r_done;
+
+ generate if (OPT_UNALIGNED)
+ begin : REALIGNMENT_FIFO
+ // {{{
+ reg [ADDRLSB-1:0] inbyte_shift, outbyte_shift,
+ remaining_read_realignment;
+ reg [ADDRLSB+3-1:0] inshift_down, outshift_down,
+ inshift_up, outshift_up;
+ reg [C_AXI_DATA_WIDTH-1:0] r_partial_inword,
+ r_outword, r_partial_outword,
+ r_realigned_incoming;
+ wire [C_AXI_DATA_WIDTH-1:0] fifo_data;
+ reg [ADDRLSB-1:0] r_last_write_addr;
+ reg r_oneword, r_firstword;
+
+
+ ///////////////////
+
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ inbyte_shift <= r_src_addr[ADDRLSB-1:0];
+ inshift_up <= 0;
+ inshift_up[3 +: ADDRLSB] <= -r_src_addr[ADDRLSB-1:0];
+ end
+
+ always @(*)
+ inshift_down = { inbyte_shift, 3'b000 };
+
+ always @(*)
+ remaining_read_realignment = -r_src_addr[ADDRLSB-1:0];
+
+ // extra_realignment_read will be true if we need to flush
+ // the read processor after the last word has been read in an
+ // extra write to the FIFO that isn't associated with any reads.
+ // In other words, if the number of writes to the FIFO is
+ // greater than the number of read beats
+ // - (src_addr unaligned?1:0)
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ extra_realignment_read <= (remaining_read_realignment
+ >= r_len[ADDRLSB-1:0]) ? 1:0;
+ if (r_len[ADDRLSB-1:0] == 0)
+ extra_realignment_read <= 1'b0;
+ if (r_src_addr[ADDRLSB-1:0] == 0)
+ extra_realignment_read <= 1'b0;
+ end else if ((!r_write_fifo || !fifo_full) && clear_read_pipeline)
+ extra_realignment_read <= 1'b0;
+
+ always @(posedge i_clk)
+ if (!r_busy || !extra_realignment_read || clear_read_pipeline)
+ clear_read_pipeline <= 0;
+ else if (!r_write_fifo || !fifo_full)
+ clear_read_pipeline <= (read_beats_remaining_w
+ == (M_AXI_RVALID ? 1:0));
+
+`ifdef FORMAL
+ always @(*)
+ if (r_busy)
+ begin
+ if (!extra_realignment_read)
+ begin
+ assert(!clear_read_pipeline);
+ end else if (read_beats_remaining_w > 0)
+ begin
+ assert(!clear_read_pipeline);
+ end else if (!no_read_bursts_outstanding)
+ begin
+ assert(!clear_read_pipeline);
+ end
+ end
+`endif
+
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_partial_in_valid <= (r_src_addr[ADDRLSB-1:0] == 0);
+ else if (M_AXI_RVALID)
+ r_partial_in_valid <= 1;
+ else if ((!r_write_fifo || !fifo_full) && clear_read_pipeline)
+ // If we have to flush the final partial valid signal,
+ // the do it when writing to the FIFO with clear_read
+ // pipelin set. Actually, this is one clock before
+ // that ...
+ r_partial_in_valid <= 0;
+
+ always @(posedge i_clk)
+ if (fifo_reset || (inbyte_shift == 0))
+ r_partial_inword <= 0;
+ else if (M_AXI_RVALID)
+ r_partial_inword <= M_AXI_RDATA >> inshift_down;
+
+ initial r_write_fifo = 0;
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_write_fifo <= 0;
+ else if (M_AXI_RVALID || clear_read_pipeline)
+ r_write_fifo <= r_partial_in_valid;
+ else if (!fifo_full)
+ r_write_fifo <= 0;
+
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_realigned_incoming <= 0;
+ else if (M_AXI_RVALID)
+ r_realigned_incoming <= r_partial_inword
+ | (M_AXI_RDATA << inshift_up);
+ else if (!r_write_fifo || !fifo_full)
+ r_realigned_incoming <= r_partial_inword;
+
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO),
+ .OPT_ASYNC_READ(1'b1)
+ // }}}
+ ) middata(
+ // {{{
+ i_clk, fifo_reset,
+ r_write_fifo, r_realigned_incoming,
+ fifo_full, fifo_fill,
+ r_read_fifo, fifo_data, fifo_empty
+ // }}}
+ );
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ outbyte_shift <= r_dst_addr[ADDRLSB-1:0];
+ outshift_down <= 0;
+ outshift_down[3 +: ADDRLSB] <= -r_dst_addr[ADDRLSB-1:0];
+ end
+
+ always @(*)
+ outshift_up = { outbyte_shift, 3'b000 };
+
+
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_partial_outword <= 0;
+ else if (r_read_fifo && outshift_up != 0)
+ r_partial_outword
+ <= (fifo_data >> outshift_down);
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ r_partial_outword <= 0;
+
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_partial_outvalid <= 0;
+ else if (r_read_fifo && !fifo_empty)
+ r_partial_outvalid <= 1;
+ else if (fifo_empty && M_AXI_WVALID && M_AXI_WREADY)
+ r_partial_outvalid <= extra_realignment_write;
+
+ always @(posedge i_clk)
+ if (fifo_reset)
+ r_outword <= 0;
+ else if (!r_partial_outvalid || (M_AXI_WVALID && M_AXI_WREADY))
+ begin
+ if (!fifo_empty)
+ r_outword <= r_partial_outword |
+ (fifo_data << outshift_up);
+ else
+ r_outword <= r_partial_outword;
+ end
+
+ always @(*)
+ M_AXI_WDATA = r_outword;
+
+ always @(*)
+ begin
+ r_read_fifo = 0;
+ if (!r_partial_outvalid)
+ r_read_fifo = 1;
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ r_read_fifo = 1;
+
+ if (fifo_empty)
+ r_read_fifo = 0;
+ end
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Write strobe logic for the unaligned case
+ //
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ if (r_len[(LGLEN-1):(ADDRLSB+1)] != 0)
+ r_oneword <= 0;
+ else
+ r_oneword <= (({ 2'b0, r_dst_addr[ADDRLSB-1:0]}
+ + r_len[ADDRLSB+1:0]) <=
+ { 2'b01, {(ADDRLSB){1'b0}} } ? 1:0);
+ end
+
+ initial r_first_wstrb = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ if (r_len[LGLEN-1:ADDRLSB] != 0)
+ r_first_wstrb <= -1 << r_dst_addr[ADDRLSB-1:0];
+ else
+ r_first_wstrb <= ((1<<r_len[ADDRLSB-1:0]) -1) << r_dst_addr[ADDRLSB-1:0];
+ end
+
+ always @(*)
+ r_last_write_addr = r_dst_addr[ADDRLSB-1:0] + r_len[ADDRLSB-1:0];
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ if (r_last_write_addr[ADDRLSB-1:0] == 0)
+ r_last_wstrb <= -1;
+ else
+ r_last_wstrb <= (1<<r_last_write_addr)-1;
+ end
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ r_firstword <= 1;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ r_firstword <= 0;
+
+ always @(posedge i_clk)
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (r_oneword)
+ M_AXI_WSTRB <= r_first_wstrb & r_last_wstrb;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ if (write_beats_remaining > 2)
+ M_AXI_WSTRB <= -1;
+ else
+ M_AXI_WSTRB <= r_last_wstrb;
+ end else if (r_firstword)
+ M_AXI_WSTRB <= r_first_wstrb;
+
+ if (r_err || r_abort)
+ M_AXI_WSTRB <= 0;
+ end
+ // }}}
+ end else begin : ALIGNED_FIFO
+ // {{{
+ always @(*)
+ begin
+ r_first_wstrb = -1;
+ r_last_wstrb = -1;
+ r_partial_in_valid = 1;
+ r_partial_outvalid = !fifo_empty;
+
+ r_write_fifo = M_AXI_RVALID;
+ r_read_fifo = M_AXI_WVALID && M_AXI_WREADY;
+
+ clear_read_pipeline = 1'b0;
+ end
+
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO),
+ .OPT_ASYNC_READ(1'b1)
+ // }}}
+ ) middata(
+ // {{{
+ i_clk, fifo_reset,
+ r_write_fifo, M_AXI_RDATA, fifo_full, fifo_fill,
+ r_read_fifo, M_AXI_WDATA, fifo_empty
+ // }}}
+ );
+
+
+ initial M_AXI_WSTRB = -1;
+ always @(posedge i_clk)
+ if (!S_AXI_ARESETN || !r_busy)
+ M_AXI_WSTRB <= -1;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ M_AXI_WSTRB <= (r_err || r_abort) ? 0 : -1;
+
+ always @(*)
+ extra_realignment_read <= 0;
+ // }}}
+ end endgenerate
+
+ // fifo_space_available
+ // {{{
+ always @(posedge i_clk)
+ if (fifo_reset)
+ fifo_space_available <= (1<<LGFIFO)
+ // space for r_partial_outvalid
+ + (OPT_UNALIGNED ? 1:0)
+ // space for r_partial_in_valid
+ + (OPT_UNALIGNED && (r_src_addr[ADDRLSB-1:0] != 0) ? 1:0);
+ else case({ phantom_read, M_AXI_WVALID && M_AXI_WREADY })
+ // Verilator lint_off WIDTH
+ 2'b10: fifo_space_available <= fifo_space_available - (M_AXI_ARLEN+1);
+ 2'b01: fifo_space_available <= fifo_space_available + 1;
+ 2'b11: fifo_space_available <= fifo_space_available - M_AXI_ARLEN;
+ // Verilator lint_on WIDTH
+ default: begin end
+ endcase
+ // }}}
+
+ // write_realignment
+ // {{{
+ always @(*)
+ if (OPT_UNALIGNED)
+ begin
+ // Verilator lint_off WIDTH
+ // write_remaining
+ write_realignment[ADDRLSB+1:0]
+ = r_len[ADDRLSB-1:0]+r_dst_addr[ADDRLSB-1:0]
+ + (1<<ADDRLSB)-1;
+
+ // Raw length
+ write_realignment[2*ADDRLSB+2:ADDRLSB+2]
+ = r_len[ADDRLSB-1:0] + (1<<ADDRLSB)-1;
+ // Verilator lint_on WIDTH
+ end else
+ write_realignment = 0;
+ // }}}
+
+ // extra_realignment_write
+ // {{{
+ always @(posedge i_clk)
+ if (!OPT_UNALIGNED)
+ extra_realignment_write <= 1'b0;
+ else if (!r_busy)
+ begin
+ if ({ 1'b0, write_realignment[2*ADDRLSB+2] }
+ != write_realignment[ADDRLSB+1:ADDRLSB])
+ extra_realignment_write <= 1'b1;
+ else
+ extra_realignment_write <= 1'b0;
+ end else if (M_AXI_WVALID && M_AXI_WREADY && fifo_empty)
+ extra_realignment_write <= 1'b0;
+ // }}}
+
+ // last_read_beat
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ last_read_beat <= 1'b0;
+ else
+ last_read_beat <= M_AXI_RVALID && M_AXI_RREADY
+ && (read_beats_remaining_w == 1);
+ // }}}
+
+ // next_fifo_data_available
+ // {{{
+ always @(*)
+ begin
+ next_fifo_data_available = fifo_data_available;
+ // Verilator lint_off WIDTH
+ if (phantom_write)
+ next_fifo_data_available = next_fifo_data_available
+ - (M_AXI_AWLEN + (r_write_fifo && !fifo_full ? 0:1));
+ else if (r_write_fifo && !fifo_full)
+ next_fifo_data_available = next_fifo_data_available + 1;
+
+ if (extra_realignment_write && last_read_beat)
+ next_fifo_data_available = next_fifo_data_available + 1;
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // fifo_data_available
+ // {{{
+ initial fifo_data_available = 0;
+ always @(posedge i_clk)
+ if (!r_busy || r_done)
+ fifo_data_available <= 0;
+ else
+ fifo_data_available <= next_fifo_data_available;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI write processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write data from the FIFO to the AXI bus
+ //
+
+ // write_address
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ write_address <= { 1'b0, r_dst_addr };
+ else if (phantom_write)
+ begin
+ // Verilator lint_off WIDTH
+ write_address <= write_address + ((M_AXI_AWLEN+1)<< M_AXI_AWSIZE);
+ // Verilator lint_on WIDTH
+ write_address[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // writes_remaining_w, multiple_write_bursts_remaining
+ // {{{
+ // Verilator lint_off WIDTH
+ always @(*)
+ w_writes_remaining_w = writes_remaining_w - (M_AXI_AWLEN+1);
+
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ writes_remaining_w <= writelen_b[LGLEN:ADDRLSB];
+ multiple_write_bursts_remaining <= |writelen_b[LGLEN:(ADDRLSB+LGMAXBURST)];
+ end else if (phantom_write)
+ begin
+ writes_remaining_w <= w_writes_remaining_w;
+ multiple_write_bursts_remaining
+ <= |w_writes_remaining_w[LGLENW:LGMAXBURST];
+ end
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // write_beats_remaining
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ write_beats_remaining <= writelen_b[LGLEN:ADDRLSB];
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ write_beats_remaining <= write_beats_remaining - 1;
+ // }}}
+
+ // write_requests_remaining
+ // {{{
+ // Verilator lint_off WIDTH
+ initial write_requests_remaining = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ write_requests_remaining <= 1'b0;
+ else if (!r_busy)
+ write_requests_remaining <= w_start;
+ else if (phantom_write)
+ write_requests_remaining <= (writes_remaining_w != (M_AXI_AWLEN+1));
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // write_bursts_outstanding
+ // {{{
+ initial write_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ write_bursts_outstanding <= 0;
+ end else case({phantom_write, M_AXI_BVALID && M_AXI_BREADY })
+ 2'b01: write_bursts_outstanding <= write_bursts_outstanding - 1;
+ 2'b10: write_bursts_outstanding <= write_bursts_outstanding + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // last_write_ack
+ // {{{
+ // Verilator lint_off WIDTH
+ always @(posedge i_clk)
+ if (!r_busy)
+ last_write_ack <= 0;
+ else if (writes_remaining_w > ((phantom_write) ? (M_AXI_AWLEN+1) : 0))
+ last_write_ack <= 0;
+ else
+ last_write_ack <= (write_bursts_outstanding
+ == (phantom_write ? 0:1) + (M_AXI_BVALID ? 1:0));
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // r_done
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy || M_AXI_ARVALID || M_AXI_AWVALID)
+ r_done <= 0;
+ else if (read_bursts_outstanding > 0)
+ r_done <= 0;
+ else if (write_bursts_outstanding > (M_AXI_BVALID ? 1:0))
+ r_done <= 0;
+ else if (r_abort || r_err)
+ r_done <= 1;
+ else if (writes_remaining_w > 0)
+ r_done <= 0;
+ else
+ r_done <= 1;
+ // }}}
+
+ // writelen_b
+ // {{{
+ always @(*)
+ if (OPT_UNALIGNED)
+ writelen_b = { 1'b0, r_len } + { {(LGLEN+1-ADDRLSB){1'b0}},
+ r_dst_addr[ADDRLSB-1:0] }
+ + { {(LGLEN+1-ADDRLSB){1'b0}}, {(ADDRLSB){1'b1}} };
+ // was + (1<<ADDRLSB)-1;
+ else begin
+ writelen_b = { 1'b0, r_len };
+ writelen_b[ADDRLSB-1:0] = 0;
+ end
+ // }}}
+
+ // initial_write_distance_to_boundary_w
+ // {{{
+ always @(*)
+ begin
+ initial_write_distance_to_boundary_w
+ = - { 1'b0, write_address[ADDRLSB +: LGMAXBURST] };
+ initial_write_distance_to_boundary_w[LGMAXBURST] = 1'b0;
+ end
+ // }}}
+
+ // first_write_burst, first_write_len_w
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ first_write_burst <= 1'b1;
+ if (|writelen_b[LGLEN:ADDRLSB+LGMAXBURST])
+ first_write_len_w <= MAXBURST;
+ else
+ first_write_len_w <= { 1'b0,
+ writelen_b[ADDRLSB +: LGMAXBURST] };
+ end else begin
+ if (phantom_write)
+ first_write_burst <= 1'b0;
+
+ if (first_write_burst
+ && write_address[ADDRLSB +: LGMAXBURST] != 0
+ && first_write_len_w
+ > initial_write_distance_to_boundary_w)
+ first_write_len_w<=initial_write_distance_to_boundary_w;
+ end
+ // }}}
+
+ // write_burst_length
+ // {{{
+ // Verilator lint_off WIDTH
+ always @(*)
+ if (first_write_burst)
+ write_burst_length = first_write_len_w;
+ else begin
+ write_burst_length = MAXBURST;
+
+ if (!multiple_write_bursts_remaining
+ && (write_burst_length[ADDRLSB +: LGMAXBURST]
+ > writes_remaining_w[ADDRLSB +: LGMAXBURST]))
+ write_burst_length = writes_remaining_w;
+ end
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // write_count
+ // {{{
+ initial write_count = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ write_count <= 0;
+ else if (w_write_start)
+ begin
+ write_count <= 0;
+ write_count[LGMAXBURST:0] <= write_burst_length[LGMAXBURST:0];
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ write_count <= write_count - 1;
+ // }}}
+
+ // M_AXI_WLAST
+ // {{{
+ initial M_AXI_WLAST = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ begin
+ M_AXI_WLAST <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ M_AXI_WLAST <= 1;
+ if (write_count > 2)
+ M_AXI_WLAST <= 0;
+ if (w_write_start)
+`ifdef AXI3
+ M_AXI_WLAST <= (write_burst_length[3:0] == 1);
+`else
+ M_AXI_WLAST <= (write_burst_length[7:0] == 1);
+`endif
+ end
+ // }}}
+
+ // w_write_start
+ // {{{
+ always @(*)
+ last_write_burst = (write_burst_length == writes_remaining_w);
+
+ always @(*)
+ begin
+ // Verilator lint_off WIDTH
+ if (!last_write_burst && OPT_UNALIGNED)
+ w_write_start = (fifo_data_available > 1)
+ &&(fifo_data_available > write_burst_length);
+ else
+ w_write_start = (fifo_data_available >= write_burst_length);
+ // Verilator lint_on WIDTH
+ if (!write_requests_remaining)
+ w_write_start = 0;
+ if (!OPT_WRAPMEM && write_address[C_AXI_ADDR_WIDTH])
+ w_write_start = 0;
+ if (phantom_write)
+ w_write_start = 0;
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ w_write_start = 0;
+ if (M_AXI_WVALID && (!M_AXI_WLAST || !M_AXI_WREADY))
+ w_write_start = 0;
+ if (i_reset || r_err || r_abort || !r_busy)
+ w_write_start = 0;
+ end
+ // }}}
+
+ // M_AXI_AWVALID, phantom_write
+ // {{{
+ initial M_AXI_AWVALID = 0;
+ initial phantom_write = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ M_AXI_AWVALID <= 0;
+ phantom_write <= 0;
+ end else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ M_AXI_AWVALID <= w_write_start;
+ phantom_write <= w_write_start;
+ end else
+ phantom_write <= 0;
+ // }}}
+
+ // M_AXI_WVALID
+ // {{{
+ initial M_AXI_WVALID = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ M_AXI_WVALID <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ M_AXI_WVALID <= 0;
+ if (M_AXI_WVALID && !M_AXI_WLAST)
+ M_AXI_WVALID <= 1;
+ if (w_write_start)
+ M_AXI_WVALID <= 1;
+ end
+ // }}}
+
+ // M_AXI_AWLEN
+ // {{{
+ initial M_AXI_AWLEN = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ M_AXI_AWLEN <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ M_AXI_AWLEN <= 0;
+ if (w_write_start)
+`ifdef AXI3
+ M_AXI_AWLEN <= write_burst_length[3:0]-1;
+`else
+ M_AXI_AWLEN <= write_burst_length[7:0]-1;
+`endif
+ end
+ // }}}
+
+ // M_AXI_AWADDR
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ M_AXI_AWADDR <= r_dst_addr;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ M_AXI_AWADDR <= write_address[C_AXI_ADDR_WIDTH-1:0];
+ // }}}
+
+ always @(*)
+ begin
+ M_AXI_AWID = AXI_WRITE_ID;
+ M_AXI_AWBURST = AXI_INCR;
+ M_AXI_AWSIZE = ADDRLSB[2:0];
+ M_AXI_AWLOCK = 1'b0;
+ M_AXI_AWCACHE = 4'b0011;
+ M_AXI_AWPROT = r_prot;
+ M_AXI_AWQOS = r_qos;
+ //
+`ifdef AXI3
+ M_AXI_WID = AXI_WRITE_ID;
+`endif
+ M_AXI_BREADY = !r_done;
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Optional) Clock gating
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_CLKGATE)
+ begin : CLK_GATING
+ // {{{
+ reg r_clk_active;
+ reg gaten /* verilator clock_enable */;
+
+ // clk_active
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clk_active <= 1'b1;
+ else begin
+ r_clk_active <= 1'b0;
+
+ if (r_busy)
+ r_clk_active <= 1'b1;
+ if (awskd_valid || wskd_valid || arskd_valid)
+ r_clk_active <= 1'b1;
+ if (S_AXIL_BVALID || S_AXIL_RVALID)
+ r_clk_active <= 1'b1;
+ end
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // Gate the clock here locally
+ // {{{
+ always @(negedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gaten <= 1'b1;
+ else
+ gaten <= r_clk_active;
+
+ assign gated_clk = S_AXI_ACLK && gaten;
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // }}}
+ end else begin : NO_CLK_GATING
+ // {{{
+ // Always active
+ assign clk_active = 1'b1;
+ assign gated_clk = S_AXI_ACLK;
+ // }}}
+ end endgenerate
+ // }}}
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ // Verilator coverage_off
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_BID,
+ M_AXI_RID, M_AXI_BRESP[0], M_AXI_RRESP[0],
+ S_AXIL_AWADDR[AXILLSB-1:0], S_AXIL_ARADDR[AXILLSB-1:0],
+ fifo_full, fifo_fill, fifo_empty,
+`ifdef AXI3
+ readlen_w[LGLENW:4],
+`else
+ readlen_w[LGLENW:8],
+`endif
+ writelen_b[ADDRLSB-1:0], readlen_b[ADDRLSB-1:0],
+ read_distance_to_boundary_b
+ };
+
+ generate if (C_AXI_ADDR_WIDTH < 2*C_AXIL_DATA_WIDTH)
+ begin : NEW_UNUSED
+ wire genunused;
+
+ assign genunused = &{ 1'b0,
+ new_widesrc[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_widedst[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] };
+ end endgenerate
+
+ // Verilator coverage_on
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The following formal properties are only a subset of those used
+ // to verify the full core. Do not be surprised to find syntax errors
+ // here, or registers that are not defined. These are correct in the
+ // full version.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ //
+ // ...
+ //
+
+ reg [C_AXI_ADDR_WIDTH:0] f_next_wraddr, f_next_rdaddr;
+
+ reg [C_AXI_ADDR_WIDTH:0] f_src_addr, f_dst_addr,
+ f_read_address, f_write_address,
+ f_read_check_addr, f_write_beat_addr,
+ f_read_beat_addr;
+ reg [LGLEN:0] f_length, f_rdlength, f_wrlength,
+ f_writes_complete, f_reads_complete;
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxil_slave #(
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH)
+ //
+ // ...
+ //
+ )
+ faxil(
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr(S_AXIL_AWADDR),
+ .i_axi_awprot(S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP)
+ //
+ // ...
+ //
+ );
+
+ //
+ // ...
+ //
+
+ //
+ // Verify the AXI-lite result registers
+ //
+ always @(*)
+ if (!r_busy)
+ assert((r_done && (r_err || r_abort))
+ || (zero_len == (r_len == 0)));
+ else
+ assert(zero_len == (r_len == 0 && writes_remaining_w == 0));
+
+ always @(*)
+ if (!i_reset && !OPT_UNALIGNED)
+ assert(r_src_addr[ADDRLSB-1:0] == 0);
+
+ always @(*)
+ if (!i_reset && !OPT_UNALIGNED)
+ assert(r_dst_addr[ADDRLSB-1:0] == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The main AXI data interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxi_master #(
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_MAXBURST(MAXBURST)
+ //
+ // ...
+ //
+ )
+ faxi(
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awid( M_AXI_AWID),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awlen( M_AXI_AWLEN),
+ .i_axi_awsize( M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock( M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot( M_AXI_AWPROT),
+ .i_axi_awqos( M_AXI_AWQOS),
+ //
+ .i_axi_wvalid(M_AXI_WVALID),
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ .i_axi_wlast( M_AXI_WLAST),
+ //
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid( M_AXI_BID),
+ .i_axi_bresp( M_AXI_BRESP),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_arid( M_AXI_ARID),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arlen( M_AXI_ARLEN),
+ .i_axi_arsize( M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock( M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot( M_AXI_ARPROT),
+ .i_axi_arqos( M_AXI_ARQOS),
+ //
+ //
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rid( M_AXI_RID),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rlast( M_AXI_RLAST),
+ .i_axi_rresp( M_AXI_RRESP)
+ //
+ // ...
+ //
+ );
+
+ always @(*)
+ begin
+ //
+ // ...
+ //
+ if (r_done)
+ begin
+ //
+ // ...
+ //
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+ end
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal assertions (Induction)
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ if (w_start)
+ begin
+ f_src_addr <= { 1'b0, r_src_addr };
+ f_dst_addr <= { 1'b0, r_dst_addr };
+ f_length <= r_len;
+ end else if (r_busy)
+ begin
+ assert(f_length != 0);
+ assert(f_length[LGLEN] == 0);
+
+ assert(f_src_addr[C_AXI_ADDR_WIDTH] == 1'b0);
+ assert(f_dst_addr[C_AXI_ADDR_WIDTH] == 1'b0);
+ end
+
+ always @(*)
+ begin
+ f_rdlength = f_length + f_src_addr[ADDRLSB-1:0]
+ + (1<<ADDRLSB)-1;
+ f_rdlength[ADDRLSB-1:0] = 0;
+
+ f_wrlength = f_length + f_dst_addr[ADDRLSB-1:0]
+ + (1<<ADDRLSB)-1;
+ f_wrlength[ADDRLSB-1:0] = 0;
+
+ f_raw_length = f_length + (1<<ADDRLSB)-1;
+ f_raw_length[ADDRLSB-1:0] = 0;
+
+ // One past the last address we'll actually read
+ f_last_src_addr = f_src_addr + f_length + (1<<ADDRLSB)-1;
+ f_last_src_addr[ADDRLSB-1:0] = 0;
+ f_last_src_beat = f_src_addr + f_length -1;
+ f_last_src_beat[ADDRLSB-1:0] = 0;
+
+ f_last_dst_addr = f_dst_addr + f_length + (1<<ADDRLSB)-1;
+ f_last_dst_addr[ADDRLSB-1:0] = 0;
+
+ f_rd_arfirst = ({ 1'b0, M_AXI_ARADDR } == f_src_addr);
+ f_rd_arlast = (M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + (M_AXI_ARLEN+1)
+ == f_last_src_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]);
+ f_rd_ckfirst = (faxi_rd_ckaddr == f_src_addr);
+ f_rd_cklast = (faxi_rd_ckaddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + faxi_rd_cklen == f_last_src_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]);
+
+ // f_extra_realignment_read
+ // true if we need to write a partial word to the FIFO/buffer
+ // *after* all of our reads are complete. This is a final
+ // flush sort of thing.
+ if (OPT_UNALIGNED && f_src_addr[ADDRLSB-1:0] != 0)
+ begin
+ // In general, if we are 1) unaligned, and 2) the
+ // length is greater than a word, and 3) there's a
+ // fraction of a word remaining, then we need to flush
+ // a last word into the FIFO.
+ f_extra_realignment_read
+ = (((1<<ADDRLSB)-f_src_addr[ADDRLSB-1:0])
+ >= f_length[ADDRLSB-1:0]) ? 1:0;
+ if (f_length[ADDRLSB-1:0] == 0)
+ f_extra_realignment_read = 0;
+ end else
+ f_extra_realignment_read = 1'b0;
+
+ //
+ // f_extra_realignment_preread
+ // will be true anytime we need to read a first word before
+ // writing anything to the buffer.
+ if (OPT_UNALIGNED && f_src_addr[ADDRLSB-1:0] != 0)
+ f_extra_realignment_preread = 1'b1;
+ else
+ f_extra_realignment_preread = 1'b0;
+
+ //
+ // f_extra_realignment_write
+ // true if following the last read from the FIFO there's a
+ // partial word that will need to be flushed through the
+ // system.
+ if (OPT_UNALIGNED && f_raw_length[LGLEN:ADDRLSB]
+ != f_wrlength[LGLEN:ADDRLSB])
+ f_extra_realignment_write = 1'b1;
+ else
+ f_extra_realignment_write = 1'b0;
+
+ f_extra_write_in_fifo = (f_extra_realignment_write)
+ && (read_beats_remaining_w == 0)&&(!last_read_beat);
+ end
+
+ always @(*)
+ if (r_busy && !OPT_UNALIGNED)
+ begin
+ assert(f_src_addr[ADDRLSB-1:0] == 0);
+ assert(f_dst_addr[ADDRLSB-1:0] == 0);
+ assert(f_length[ADDRLSB-1:0] == 0);
+ end
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!f_extra_realignment_write)
+ assert(!extra_realignment_write);
+ else if (f_writes_complete >= f_wrlength[LGLEN:ADDRLSB]-1)
+ assert(!extra_realignment_write);
+ else
+ assert(extra_realignment_write);
+
+ if ((f_writes_complete > 0 || M_AXI_WVALID)
+ && extra_realignment_write)
+ assert(r_partial_outvalid);
+ end
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (extra_realignment_read)
+ assert(f_extra_realignment_read);
+ else if (read_beats_remaining_w > 0)
+ assert(f_extra_realignment_read == extra_realignment_read);
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (fifo_full)
+ assert(no_read_bursts_outstanding);
+
+ always @(*)
+ if (r_busy)
+ assert(!r_int);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write checks
+ //
+
+ //
+ // ...
+ //
+
+ always @(*)
+ begin
+ f_write_address = 0;
+ f_write_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_dst_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + (f_wrlength[LGLEN:ADDRLSB] - writes_remaining_w);
+ end
+
+ always @(*)
+ begin
+ f_next_wraddr = { 1'b0, M_AXI_AWADDR }
+ + ((M_AXI_AWLEN+1)<<M_AXI_AWSIZE);
+ f_next_wraddr[ADDRLSB-1:0] = 0;
+ end
+
+ always @(*)
+ if (M_AXI_AWVALID)
+ begin
+ assert(M_AXI_WVALID);
+ assert(M_AXI_AWLEN < (1<<LGMAXBURST));
+
+ if (M_AXI_AWLEN != (1<<LGMAXBURST)-1)
+ begin
+ assert((M_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ == f_dst_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB])
+ ||(phantom_write)
+ ||(writes_remaining_w == 0));
+ end else begin
+ assert(M_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ == f_write_beat_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]);
+ end
+
+ if (M_AXI_AWADDR[ADDRLSB-1:0] != 0)
+ assert({ 1'b0, M_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:0] }
+ == f_dst_addr);
+ else if (M_AXI_AWADDR != f_dst_addr)
+ assert(M_AXI_AWADDR[0 +: LGMAXBURST+ADDRLSB] == 0);
+ end
+
+ always @(*)
+ if (!OPT_UNALIGNED)
+ begin
+ assert(r_len[ADDRLSB-1:0] == 0);
+ assert(r_src_addr[ADDRLSB-1:0] == 0);
+ assert(r_dst_addr[ADDRLSB-1:0] == 0);
+ end
+
+ always @(*)
+ if (r_busy)
+ begin
+ assert(writes_remaining_w <= f_wrlength[LGLEN:ADDRLSB]);
+ assert(f_writes_complete <= f_wrlength[LGLEN:ADDRLSB]);
+ assert(fifo_fill <= f_wrlength[LGLEN:ADDRLSB]);
+
+ assert(write_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ == f_write_address[C_AXI_ADDR_WIDTH:ADDRLSB]);
+
+ if (M_AXI_AWADDR != f_dst_addr && writes_remaining_w != 0)
+ assert(M_AXI_AWADDR[LGMAXBURST+ADDRLSB-1:0] == 0);
+ else if (!OPT_UNALIGNED)
+ assert(M_AXI_AWADDR[ADDRLSB-1:0] == 0);
+
+ if((writes_remaining_w!= f_wrlength[LGLEN:ADDRLSB])
+ &&(writes_remaining_w != 0))
+ assert(write_address[LGMAXBURST+ADDRLSB-1:0] == 0);
+
+ if ((write_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ != f_dst_addr[C_AXI_ADDR_WIDTH:ADDRLSB])
+ &&(writes_remaining_w > 0))
+ assert(write_address[LGMAXBURST+ADDRLSB-1:0] == 0);
+
+ if (writes_remaining_w != f_wrlength[LGLEN:ADDRLSB]
+ && writes_remaining_w != 0)
+ assert((write_burst_length == (1<<LGMAXBURST))
+ ||(write_burst_length == writes_remaining_w));
+
+ assert(write_requests_remaining == (writes_remaining_w != 0));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (M_AXI_AWVALID && !phantom_write)
+ begin
+ assert(write_count == (M_AXI_AWLEN+1));
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ if (r_busy && last_write_ack)
+ begin
+ assert(reads_remaining_w == 0);
+ assert(!M_AXI_ARVALID);
+ assert(writes_remaining_w == 0);
+ end
+
+ always @(*)
+ if (r_busy && !r_abort && !r_err)
+ assert(write_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ == f_dst_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ || (write_address[LGMAXBURST-1:0] == 0)
+ || (writes_remaining_w == 0));
+
+ always @(*)
+ if (phantom_write)
+ assert(writes_remaining_w >= (M_AXI_AWLEN+1));
+ else if (M_AXI_AWVALID)
+ assert(write_address[C_AXI_ADDR_WIDTH-1:0]
+ == f_next_wraddr[C_AXI_ADDR_WIDTH-1:0]);
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (M_AXI_WVALID)
+ begin
+ if (OPT_UNALIGNED)
+ assert(r_partial_outvalid);
+ else
+ assert(!fifo_empty || r_abort || r_err);
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ begin
+ f_write_beat_addr = 0;
+ f_write_beat_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_dst_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + (f_wrlength[LGLEN:ADDRLSB]-write_beats_remaining);
+
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ if (r_busy)
+ begin
+ //
+ // ...
+ //
+
+ if (write_beats_remaining == 0)
+ assert(!M_AXI_WVALID);
+ end
+
+ always @(*)
+ if (writes_remaining_w < f_wrlength[LGLEN:ADDRLSB])
+ begin
+ if (writes_remaining_w == 0)
+ assert(fifo_data_available == 0);
+ // else ...
+ end
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read checks
+ //
+ always @(*)
+ begin
+ f_reads_complete = f_rdlength[LGLEN:ADDRLSB]
+ - reads_remaining_w - // ...
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ begin
+ if (reads_remaining_w == f_rdlength[LGLEN:ADDRLSB])
+ f_read_address = f_src_addr;
+ else begin
+ f_read_address = 0;
+ f_read_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_src_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + (f_rdlength[LGLEN:ADDRLSB] - reads_remaining_w);
+ end
+
+ f_araddr = f_read_address;
+ if (M_AXI_ARVALID && !phantom_read)
+ f_araddr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_araddr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ - (M_AXI_ARLEN+1);
+ if (f_araddr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ == f_src_addr[C_AXI_ADDR_WIDTH:ADDRLSB])
+ f_araddr[ADDRLSB-1:0] = f_src_addr[ADDRLSB-1:0];
+
+ //
+ // Match the read check address to our source address
+ //
+ f_read_check_addr = 0;
+ f_read_check_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ = f_src_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + f_reads_complete + // ...
+
+
+ //
+ // Match the RVALID address to our source address
+ //
+ f_read_beat_addr = 0;
+ if (f_reads_complete == 0)
+ f_read_beat_addr = f_src_addr;
+ else
+ f_read_beat_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_src_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + f_reads_complete;
+
+ f_end_of_read_burst = M_AXI_ARADDR;
+ f_end_of_read_burst[ADDRLSB-1:0] = 0;
+ f_end_of_read_burst[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_end_of_read_burst[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + (M_AXI_ARLEN + 1);
+
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ begin
+ f_next_rdaddr = { 1'b0, M_AXI_ARADDR } + ((M_AXI_ARLEN+1)<<M_AXI_ARSIZE);
+ f_next_rdaddr[ADDRLSB-1:0] = 0;
+ end
+
+ /////////
+ //
+ // Constrain the read address
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!f_rd_arfirst)
+ begin
+ assert(reads_remaining_w < f_rdlength[LGLEN:ADDRLSB]);
+
+ // All bursts following the first one must be aligned
+ if (M_AXI_ARVALID || reads_remaining_w > 0)
+ assert(M_AXI_ARADDR[0 +: LGMAXBURST + ADDRLSB] == 0);
+ end else if (phantom_read)
+ assert(reads_remaining_w == f_rdlength[LGLEN:ADDRLSB]);
+ else if (M_AXI_ARVALID)
+ assert(reads_remaining_w == f_rdlength[LGLEN:ADDRLSB]
+ - (M_AXI_ARLEN+1));
+
+ // All but the first burst should be aligned
+ if (!OPT_UNALIGNED || M_AXI_ARADDR != f_src_addr)
+ assert(M_AXI_ARADDR[ADDRLSB-1:0] == 0);
+
+ if (phantom_read)
+ assert(M_AXI_ARADDR == read_address[C_AXI_ADDR_WIDTH-1:0]);
+ else if (M_AXI_ARVALID)
+ assert(read_address[C_AXI_ADDR_WIDTH-1:0] == f_next_rdaddr[C_AXI_ADDR_WIDTH-1:0]);
+ end // else ...
+
+ //
+ // ...
+ //
+
+ // Constrain read_address--our pointer to the next bursts address
+ always @(*)
+ if (r_busy)
+ begin
+ assert((read_address == f_src_addr)
+ || (read_address[LGMAXBURST+ADDRLSB-1:0] == 0)
+ || (reads_remaining_w == 0));
+
+
+ assert(read_address == f_read_address);
+ if (!OPT_UNALIGNED)
+ assert(read_address[ADDRLSB-1:0] == 0);
+
+ if ((reads_remaining_w != f_rdlength[LGLEN:ADDRLSB])
+ &&(reads_remaining_w > 0))
+ assert(read_address[LGMAXBURST+ADDRLSB-1:0] == 0);
+
+ if ((read_address[C_AXI_ADDR_WIDTH:ADDRLSB]
+ != f_src_addr[C_AXI_ADDR_WIDTH:ADDRLSB])
+ &&(reads_remaining_w > 0))
+ assert(read_address[LGMAXBURST+ADDRLSB-1:0] == 0);
+ end
+
+
+ /////////
+ //
+ // Constrain the read length
+ //
+ always @(*)
+ if (M_AXI_ARVALID)
+ begin
+ assert(M_AXI_ARLEN < (1<<LGMAXBURST));
+ if (M_AXI_ARLEN != (1<<LGMAXBURST)-1)
+ begin
+ assert((M_AXI_ARADDR == f_src_addr)
+ ||(phantom_read)
+ ||(reads_remaining_w == 0));
+ end
+
+ assert(f_end_of_read_burst_last[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]
+ == M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]);
+ end
+
+ always @(*)
+ if (M_AXI_ARVALID && reads_remaining_w > (M_AXI_ARLEN+1))
+ assert(f_end_of_read_burst[ADDRLSB+LGMAXBURST-1:0]==0);
+
+
+ /////////
+ //
+ // Constrain RLAST
+ //
+
+ always @(*)
+ if (M_AXI_RVALID)
+ begin
+ if (M_AXI_RLAST)
+ begin
+ if (f_read_beat_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ != f_last_src_beat[C_AXI_ADDR_WIDTH:ADDRLSB])
+ assert(&f_read_beat_addr[ADDRLSB+LGMAXBURST-1:ADDRLSB]);
+ end else
+ assert(f_read_beat_addr[ADDRLSB+LGMAXBURST-1:ADDRLSB]
+ != {(LGMAXBURST){1'b1}});
+ end
+
+ always @(*)
+ if (f_read_beat_addr == f_last_src_beat)
+ assert(!M_AXI_RVALID || M_AXI_RLAST);
+ else
+ assert(!M_AXI_RVALID
+ || M_AXI_RLAST == (&f_read_beat_addr[LGMAXBURST+ADDRLSB-1:ADDRLSB]));
+
+
+ /////////
+ //
+ //
+ //
+
+
+ always @(*)
+ assert(no_read_bursts_outstanding == (read_bursts_outstanding == 0));
+
+ always @(*)
+ if (r_busy && !r_err)
+ assert(f_read_beat_addr[C_AXI_ADDR_WIDTH-1:0] == r_src_addr);
+
+ always @(*)
+ if (r_busy)
+ begin
+ // Some quick checks to make sure the subsequent checks
+ // don't overflow anything
+ assert(reads_remaining_w <= f_rdlength[LGLEN:ADDRLSB]);
+ assert(f_reads_complete <= f_rdlength[LGLEN:ADDRLSB]);
+ // ...
+ assert(fifo_fill <= f_raw_length[LGLEN:ADDRLSB]);
+ assert(fifo_space_available<= (1<<LGFIFO));
+ assert(fifo_space_available<= (1<<LGFIFO) - fifo_fill);
+ // ...
+
+
+ if (reads_remaining_w != f_rdlength[LGLEN:ADDRLSB]
+ && reads_remaining_w != 0)
+ assert((readlen_w == (1<<LGMAXBURST))
+ ||(readlen_w == reads_remaining_w));
+
+ //
+ // Read-length checks
+ assert(readlen_w <= (1<<LGMAXBURST));
+ if (reads_remaining_w > 0)
+ assert(readlen_w != 0);
+ if (readlen_w != (1<<LGMAXBURST))
+ assert(reads_remaining_w == readlen_w
+ ||(read_address == f_src_addr));
+ end
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////
+ //
+ // Errors or aborts -- do we properly end gracefully
+ //
+ // Make certain we don't deadlock here, but also that we wait
+ // for the last burst return before we clear
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && r_busy && $past(r_busy))
+ begin
+ // Not allowed to set r_done while anything remains outstanding
+ if (!no_read_bursts_outstanding)
+ assert(!r_done);
+ if (write_bursts_outstanding > 0)
+ assert(!r_done);
+
+ //
+ // If no returns are outstanding, and following an error, then
+ // r_done should be set
+ if ($past(r_busy && (r_err || r_abort))
+ &&($past(!M_AXI_ARVALID && read_bursts_outstanding==0))
+ &&($past(!M_AXI_AWVALID && write_bursts_outstanding==0)))
+ assert(r_done);
+
+ if ($past(r_err || r_abort))
+ begin
+ //
+ // Just double check that we aren't starting anything
+ // new following either an abort or an error
+ //
+ assert(!$rose(M_AXI_ARVALID));
+ assert(!$rose(M_AXI_AWVALID));
+
+ if ($past(!M_AXI_WVALID || M_AXI_WREADY))
+ assert(M_AXI_WSTRB == 0);
+ end
+ end
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (readlen_w == 0)
+ assert(reads_remaining_w == 0);
+ else begin
+ assert(reads_remaining_w > 0);
+ if (!w_start_read)
+ begin
+ assert(readlen_w <= reads_remaining_w);
+ assert(readlen_w <= (1<<LGMAXBURST));
+ end
+ end
+ end
+
+ always @(*)
+ if (M_AXI_BVALID)
+ assert(M_AXI_BREADY);
+
+ always @(*)
+ if (M_AXI_RVALID)
+ assert(M_AXI_RREADY);
+
+ generate if (OPT_UNALIGNED)
+ begin : REALIGNMENT_CHECKS
+
+ //
+ // ...
+ //
+
+ end endgenerate
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FIFO checks
+ //
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (phantom_read)
+ assert(M_AXI_ARVALID);
+
+ always @(*)
+ if (phantom_write)
+ assert(M_AXI_AWVALID);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Combined
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ //
+ // ...
+ //
+
+ assert(writes_remaining_w + write_bursts_outstanding
+ <= f_wrlength[LGLEN:ADDRLSB]);
+
+ //
+ // ...
+ //
+ if (write_count > 0)
+ assert(M_AXI_WVALID);
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ if (r_busy)
+ assert(last_write_ack == ((write_bursts_outstanding <= 1)
+ &&(writes_remaining_w == 0)));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Initial (only) constraints
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(!S_AXIL_BVALID);
+ assert(!S_AXIL_RVALID);
+
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+
+ assert(write_bursts_outstanding == 0);
+ assert(write_requests_remaining == 0);
+
+ assert(!phantom_read);
+ assert(!phantom_write);
+ assert(!r_busy);
+ assert(read_bursts_outstanding == 0);
+ assert(no_read_bursts_outstanding);
+
+ assert(r_len == 0);
+ assert(zero_len);
+
+ assert(write_count == 0);
+ assert(!M_AXI_WLAST);
+ assert(M_AXI_AWLEN == 0);
+ assert(!r_write_fifo);
+ assert(r_src_addr == 0);
+ assert(r_dst_addr == 0);
+ end
+
+ always @(*)
+ assert(ADDRLSB + LGMAXBURST <= 12);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract checking
+ // {{{
+ // Given an arbitrary address within the source address range, and an
+ // arbitrary piece of data at that source address, prove that said
+ // piece of data will get properly written to the destination address
+ // range
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // We'll pick the byte read from const_addr_src, and then written to
+ // const_addr_dst.
+ //
+ generate if (OPT_UNALIGNED)
+ begin : F_UNALIGNED_SAMPLE_CHECK
+ (* anyconst *) reg [C_AXI_ADDR_WIDTH-1:0] f_const_posn;
+ reg [C_AXI_ADDR_WIDTH:0] f_const_addr_src,
+ f_const_addr_dst;
+ reg [8-1:0] f_const_byte;
+ reg [C_AXI_ADDR_WIDTH:0] f_write_fifo_addr,
+ f_read_fifo_addr,
+ f_partial_out_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] f_shifted_read, f_shifted_write;
+ reg [C_AXI_DATA_WIDTH/8-1:0] f_shifted_wstrb;
+ reg [C_AXI_DATA_WIDTH-1:0] f_shifted_to_fifo,
+ f_shifted_partial_to_fifo,
+ f_shifted_partial_from_fifo;
+ reg [C_AXI_DATA_WIDTH-1:0] f_shifted_from_fifo,
+ f_shifted_from_partial_out;
+ reg [ADDRLSB-1:0] subshift;
+
+
+ always @(*)
+ begin
+ assume(f_const_posn < f_length);
+
+ f_const_addr_src = f_src_addr + f_const_posn;
+ f_const_addr_dst = f_dst_addr + f_const_posn;
+
+ f_shifted_read =(M_AXI_RDATA >> (8*f_const_addr_src[ADDRLSB-1:0]));
+ f_shifted_write=(M_AXI_WDATA >> (8*f_const_addr_dst[ADDRLSB-1:0]));
+ f_shifted_wstrb=(M_AXI_WSTRB >> (f_const_addr_dst[ADDRLSB-1:0]));
+ end
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Step 1: Assume a known input
+ // Actually, we'll copy it when it comes in
+ //
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_RVALID
+ && f_read_beat_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ == f_const_addr_src[C_AXI_ADDR_WIDTH:ADDRLSB])
+ begin
+ // Record our byte to be read
+ f_const_byte <= f_shifted_read[7:0];
+ end
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Step 2: Assert that value gets written on the way out
+ //
+ always @(*)
+ if (M_AXI_WVALID
+ && f_write_beat_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ == f_const_addr_dst[C_AXI_ADDR_WIDTH:ADDRLSB])
+ begin
+ // Assert that we have the right byte in the end
+ if (!r_err && !r_abort)
+ begin
+ // Although it only really matters if we are
+ // actually writing it to the bus
+ assert(f_shifted_wstrb[0]);
+ assert(f_shifted_write[7:0] == f_const_byte);
+ end else
+ assert(f_shifted_wstrb[0] || M_AXI_WSTRB==0);
+ end
+
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Assert the write side of the realignment FIFO
+ //
+ always @(*)
+ begin
+ //
+ // ...
+ //
+
+ subshift = f_const_posn[ADDRLSB-1:0];
+ end
+
+ always @(*)
+ f_shifted_to_fifo = REALIGNMENT_FIFO.r_realigned_incoming
+ >> (8*subshift);
+
+ always @(*)
+ f_shifted_partial_to_fifo = REALIGNMENT_FIFO.r_partial_inword
+ >> (8*subshift);
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (S_AXI_ARESETN && r_write_fifo
+ && f_write_fifo_addr <= f_const_addr_src
+ && f_write_fifo_addr + (1<<ADDRLSB) > f_const_addr_src)
+ begin
+ // Assert that our special byte gets written to the FIFO
+ assert(f_const_byte == f_shifted_to_fifo[7:0]);
+ end
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Assert the read side of the realignment FIFO
+ //
+ always @(*)
+ begin
+ f_read_fifo_addr =f_dst_addr;
+ f_read_fifo_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ = f_dst_addr[C_AXI_ADDR_WIDTH:ADDRLSB]
+ + (r_partial_outvalid ? 1:0)
+ + f_writes_complete;
+
+ f_partial_out_addr = f_read_fifo_addr;
+ f_partial_out_addr[ADDRLSB-1:0] = 0;
+ end
+
+ always @(*)
+ f_shifted_from_fifo = REALIGNMENT_FIFO.fifo_data
+ >> (8*subshift);
+
+ always @(*)
+ f_shifted_from_partial_out
+ = REALIGNMENT_FIFO.r_partial_outword
+ >> (8*f_const_addr_dst[ADDRLSB-1:0]);
+
+
+ always @(*)
+ if (!fifo_empty && f_read_fifo_addr <= f_const_addr_dst
+ && f_read_fifo_addr + (1<<ADDRLSB) > f_const_addr_dst)
+ begin
+ // Assume that our special byte gets read from the FIFO
+ // That way we don't have to verify every element of the
+ // FIFO. We'll instead rely upon the FIFO working from
+ // here.
+ assume(f_const_byte == f_shifted_from_fifo[7:0]);
+ end
+
+ //
+ // ...
+ //
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [3:0] f_cvr_rd_bursts, f_cvr_wr_bursts;
+ reg f_cvr_complete;
+
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ f_cvr_wr_bursts <= 0;
+ else if (w_start)
+ f_cvr_wr_bursts <= 0;
+ else if (M_AXI_AWVALID && M_AXI_AWREADY && !f_cvr_wr_bursts[3])
+ f_cvr_wr_bursts <= f_cvr_wr_bursts + 1;
+
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ f_cvr_rd_bursts <= 0;
+ else if (w_start)
+ f_cvr_rd_bursts <= 0;
+ else if (M_AXI_ARVALID && M_AXI_ARREADY && !f_cvr_rd_bursts[3])
+ f_cvr_rd_bursts <= f_cvr_rd_bursts + 1;
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy))
+ cover(!r_busy && !r_err && !r_abort && f_cvr_wr_bursts[0]
+ && f_cvr_rd_bursts[0]);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy))
+ cover(r_done && !r_err && !r_abort && f_cvr_wr_bursts[0]
+ && f_cvr_rd_bursts[0]);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy))
+ cover(!r_busy && !r_err && !r_abort && &f_cvr_wr_bursts[1]
+ && f_cvr_rd_bursts[1]);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy))
+ cover(!r_busy && !r_err && !r_abort && (&f_cvr_wr_bursts[1:0])
+ && (&f_cvr_rd_bursts[1:0]));
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy))
+ cover(!r_busy && !r_err && !r_abort && f_cvr_wr_bursts[2]
+ && f_cvr_rd_bursts[2]);
+
+ always @(*)
+ cover(f_past_valid && S_AXI_ARESETN && r_busy && r_err);
+
+ always @(*)
+ cover(f_past_valid && S_AXI_ARESETN && r_busy
+ && M_AXI_RVALID && M_AXI_RRESP[1]);
+
+ always @(*)
+ cover(f_past_valid && S_AXI_ARESETN && r_busy
+ && M_AXI_RVALID && M_AXI_BRESP[1]);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy && r_err))
+ cover(!r_busy && r_err);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && r_busy && r_abort))
+ cover(!r_busy && r_abort);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && r_busy && !r_abort && !r_err)
+ cover(reads_remaining_w == 0);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && r_busy && !r_abort && !r_err)
+ cover(reads_remaining_w == 0 && fifo_empty);
+
+ generate if (OPT_UNALIGNED)
+ begin : COVER_MISALIGNED_COPYING
+
+ reg [2:0] cvr_opt_checks;
+ integer ik;
+
+ always @(*)
+ begin
+ cvr_opt_checks[0] = (f_src_addr[ADDRLSB-1:0] == 0);
+ cvr_opt_checks[1] = (f_dst_addr[ADDRLSB-1:0] == 0);
+ cvr_opt_checks[2] = (f_length[ ADDRLSB-1:0] == 0);
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && S_AXI_ARESETN && o_int)
+ begin
+ for(ik=0; ik<8; ik=ik+1)
+ begin
+ cover(cvr_opt_checks == ik[2:0]
+ && !r_busy && r_err && !r_abort
+ && f_cvr_wr_bursts[0]
+ && f_cvr_rd_bursts[0]);
+
+ cover(cvr_opt_checks == ik[2:0]
+ && !r_busy && !r_err && r_abort
+ && f_cvr_wr_bursts[0]
+ && f_cvr_rd_bursts[0]);
+
+ cover(cvr_opt_checks == ik[2:0]
+ && !r_busy && !r_err && !r_abort
+ && f_cvr_wr_bursts[0]
+ && f_cvr_rd_bursts[0]);
+
+ cover(cvr_opt_checks == ik[2:0]
+ && !r_busy && !r_err && !r_abort
+ && f_cvr_wr_bursts[2]
+ && f_cvr_rd_bursts[2]);
+ end
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && S_AXI_ARESETN && o_int)
+ begin
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && f_extra_realignment_read
+ && f_extra_realignment_write);
+
+ //
+ // Will never happen--since f_extra_realignment_read
+ // can only be true if f_extra_realignment_preread
+ // is also true
+ //
+ // cover(!r_busy && !r_err && !r_abort
+ // && !f_extra_realignment_preread
+ // && f_extra_realignment_read
+ // && f_extra_realignment_write);
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && f_extra_realignment_write);
+
+ cover(!r_busy && !r_err && !r_abort
+ && !f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && f_extra_realignment_write);
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && f_extra_realignment_read
+ && !f_extra_realignment_write);
+
+ // !preread && read will never happen
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && !f_extra_realignment_write);
+
+ cover(!r_busy && !r_err && !r_abort
+ && !f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && !f_extra_realignment_write);
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && f_extra_realignment_read
+ && f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+
+ // !preread && read will never happen
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+
+ cover(!r_busy && !r_err && !r_abort
+ && !f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && f_extra_realignment_read
+ && !f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+
+
+ // !preread && read will never happen
+
+ cover(!r_busy && !r_err && !r_abort
+ && f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && !f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+
+ cover(!r_busy && !r_err && !r_abort
+ && !f_extra_realignment_preread
+ && !f_extra_realignment_read
+ && !f_extra_realignment_write
+ && f_length[LGLEN-1:ADDRLSB] > 2);
+ end
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions (i.e. constraints)
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // None (currently)
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axidouble.v b/rtl/wb2axip/axidouble.v
new file mode 100644
index 0000000..6842e71
--- /dev/null
+++ b/rtl/wb2axip/axidouble.v
@@ -0,0 +1,1406 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axidouble.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Create a special AXI slave which can be used to reduce crossbar
+// logic for multiple simplified AXI slaves. This is a companion
+// core to the similar axisingle core, but allowing the slave to
+// decode the clock between multiple possible addresses.
+//
+// To use this, the slave must follow specific (simplified AXI) rules:
+//
+// Write interface
+// --------------------
+// 1. The controller will guarantee that AWVALID == WVALID
+// (You can connect AWVALID to WVALID when connecting to your core)
+// 2. The controller will guarantee that AWID == 0 for the slave
+// All ID logic will be handled internally
+// 3. The controller will guarantee that AWLEN == 0 and WLAST == 1
+// Instead, the controller will handle all burst addressing
+// internally
+// 4. This makes AWBURST irrelevant
+// 5. Other wires are simplified as well: AWLOCK=0, AWCACHE=3, AWQOS=0
+// 6. If OPT_EXCLUSIVE_ACCESS is set, the controller will handle lock
+// logic internally
+// 7. The slave must guarantee that AWREADY == WREADY = 1
+// (This core doesn't have AWREADY or WREADY inputs)
+// 8. The slave must also guarantee that BVALID == $past(AWVALID)
+// (This core internally generates BVALID, and so the slave's
+// BVALID return is actually ignored.)
+// 9. The controller will also guarantee that BREADY = 1
+// (This core doesn't have a BVALID input)
+//
+// The controller will maintain AWPROT in case the slave wants to
+// disallow particular writes, and AWSIZE so the slave can know how many
+// bytes are being accessed.
+//
+// Read interface
+// --------------------
+// 1. The controller will guarantee that RREADY = 1
+// (This core doesn't have an RREADY output)
+// 2. The controller will guarantee that ARID = 0
+// All IDs are handled internally
+// 3. The controller will guarantee that ARLEN == 0
+// All burst logic is handled internally
+// 4. As before, this makes ARBURST irrelevant
+// 5. Other wires are simplified: ARLOCK=0, ARCACHE = 3, ARQOS=0, etc
+// 6. The slave must guarantee that RVALID == $past(ARVALID)
+// The controller actually ignores RVALID--but to be a valid slave,
+// this must be assumed.
+// 7. The slave must also guarantee that RLAST == 1 anytime RVALID
+//
+// As with the write side, the controller will fill in ARSIZE and ARPROT.
+// They may be used or ignored by the slave.
+//
+// Why? This simplifies slave logic. Slaves may interact with the bus
+// using only the logic below:
+//
+// always @(posedge S_AXI_ACLK)
+// if (AWVALID) case(AWADDR)
+// R1: slvreg_1 <= WDATA;
+// R2: slvreg_2 <= WDATA;
+// R3: slvreg_3 <= WDATA;
+// R4: slvreg_4 <= WDATA;
+// endcase
+//
+// always @(*)
+// BRESP = 2'b00; // OKAY
+//
+// always @(posedge S_AXI_ACLK)
+// if (ARVALID)
+// case(ARADDR)
+// R1: RDATA <= slvreg_1;
+// R2: RDATA <= slvreg_2;
+// R3: RDATA <= slvreg_3;
+// R4: RDATA <= slvreg_4;
+// endcase
+//
+// always @(*)
+// RRESP = 2'b00; // OKAY
+//
+// This core will then keep track of the more complex bus logic, locking,
+// burst length, burst ID's, etc, simplifying both slaves and connection
+// logic. Slaves with the more complicated (and proper/accurate) logic,
+// that follow the rules above, should have no problems with this
+// additional logic.
+//
+// Performance:
+//
+// Throughput: The slave can sustain one read/write per clock as long as
+// the upstream master keeps S_AXI_[BR]READY high. If S_AXI_[BR]READY
+// ever drops, there's some flexibility provided by the return FIFO, so
+// the master might not notice a drop in throughput until the FIFO fills.
+//
+// Latency: This core will create a four clock latency on all requests.
+//
+// Logic: Actual logic depends upon how this is set up and built. As
+// parameterized below, this core can fit within 639 Xilinx 6-LUTs and
+// 39 M-LUTs.
+//
+// Narrow bursts: This core supports narrow bursts by nature. Whether the
+// subcores pay attention to WSTRB, AWSIZE, and ARSIZE is up to the
+// subcore itself.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axidouble #(
+ // {{{
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 32,
+ parameter integer C_AXI_ID_WIDTH = 1,
+ //
+ // NS is the number of slave interfaces. If you are interested
+ // in a single slave interface, checkout the demofull.v core
+ // in this same repository.
+ parameter NS = 8,
+ //
+ // OPT_LOWPOWER is generated by the interconnect, so we need
+ // to define it here.
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ //
+ // Shorthand for address width, data width, and id width
+ // AW, and DW, are short-hand abbreviations used locally.
+ localparam AW = C_AXI_ADDR_WIDTH,
+ //
+ // Each of the slave interfaces has an address range. The
+ // base address for each slave is given by AW bits of SLAVE_ADDR
+ // below.
+ parameter [NS*AW-1:0] SLAVE_ADDR = {
+ { 3'b111, {(AW-3){1'b0}} },
+ { 3'b110, {(AW-3){1'b0}} },
+ { 3'b101, {(AW-3){1'b0}} },
+ { 3'b100, {(AW-3){1'b0}} },
+ { 3'b011, {(AW-3){1'b0}} },
+ { 3'b010, {(AW-3){1'b0}} },
+ { 4'b0001,{(AW-4){1'b0}} },
+ { 4'b0000,{(AW-4){1'b0}} } },
+ //
+ //
+ // The relevant bits of the slave address are given in
+ // SLAVE_MASK below, at AW bits per slave. To be valid,
+ // SLAVE_ADDR & ~SLAVE_MASK must be zero. Only the masked
+ // bits will be used in any compare.
+ //
+ // Also, while not technically required, it is strongly
+ // recommended that the bottom 12-bits of each AW bits of
+ // the SLAVE_MASK bust be zero.
+ parameter [NS*AW-1:0] SLAVE_MASK =
+ (NS <= 1) ? 0
+ : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }},
+ {(2){ 4'b1111, {(AW-4){1'b0}} }}
+ },
+ //
+ // LGFLEN specifies the log (based two) of the number of
+ // transactions that may need to be held outstanding internally.
+ // If you really want high throughput, and if you expect any
+ // back pressure at all, then increase LGFLEN. Otherwise the
+ // default value of 3 (FIFO size = 8) should be sufficient
+ // to maintain full loading
+ parameter LGFLEN=3,
+ //
+ // This core will handle exclusive access if
+ // OPT_EXCLUSIVE_ACCESS is set to one. If set to 1, all
+ // subcores will have exclusive access applied. There is no
+ // core-by-core means of enabling exclusive access at this time.
+ parameter [0:0] OPT_EXCLUSIVE_ACCESS = 1'b1
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // Write address channel coming from upstream
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [8-1:0] S_AXI_AWLEN,
+ input wire [3-1:0] S_AXI_AWSIZE,
+ input wire [2-1:0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [4-1:0] S_AXI_AWCACHE,
+ input wire [3-1:0] S_AXI_AWPROT,
+ input wire [4-1:0] S_AXI_AWQOS,
+ //
+ // Write data channel coming from upstream
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ //
+ // Write responses sent back
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [2-1:0] S_AXI_BRESP,
+ //
+ // Read address request channel from upstream
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [8-1:0] S_AXI_ARLEN,
+ input wire [3-1:0] S_AXI_ARSIZE,
+ input wire [2-1:0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [4-1:0] S_AXI_ARCACHE,
+ input wire [3-1:0] S_AXI_ARPROT,
+ input wire [4-1:0] S_AXI_ARQOS,
+ //
+ // Read data return channel back upstream
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire S_AXI_RLAST,
+ output wire [2-1:0] S_AXI_RRESP,
+ //
+ //
+ // Now for the simplified downstream interface to a series
+ // of downstream slaves. All outgoing wires are shared between
+ // the slaves save the AWVALID and ARVALID signals. Slave
+ // returns are not shared.
+ //
+ //
+ // Simplified Write address channel.
+ output wire [NS-1:0] M_AXI_AWVALID,
+ // input wire M_AXI_AWREADY is assumed to be 1
+ output wire [0:0] M_AXI_AWID,// = 0
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [8-1:0] M_AXI_AWLEN,// = 0
+ output wire [3-1:0] M_AXI_AWSIZE,
+ output wire [2-1:0] M_AXI_AWBURST,//=INC
+ output wire M_AXI_AWLOCK,// = 0
+ output wire [4-1:0] M_AXI_AWCACHE,// = 0
+ output wire [3-1:0] M_AXI_AWPROT,// = 0
+ output wire [4-1:0] M_AXI_AWQOS,// = 0
+ //
+ // Simplified write data channel
+ output wire [NS-1:0] M_AXI_WVALID,//=AWVALID
+ // input wire M_AXI_WVALID is *assumed* to be 1
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,// = 1
+ //
+ // Simplified write response channel
+ // input wire M_AXI_BVALID is *assumed* to be
+ // $past(M_AXI_AWVALID), and so ignored
+ output wire M_AXI_BREADY,// = 1
+ input wire [NS*2-1:0] M_AXI_BRESP,
+ // The controller handles BID, so this can be ignored as well
+ //
+ // Simplified read address channel
+ output wire [NS-1:0] M_AXI_ARVALID,
+ // input wire M_AXI_ARREADY is assumed to be 1
+ output wire [0:0] M_AXI_ARID,// = 0
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [8-1:0] M_AXI_ARLEN,// = 0
+ output wire [3-1:0] M_AXI_ARSIZE,
+ output wire [2-1:0] M_AXI_ARBURST,//=INC
+ output wire M_AXI_ARLOCK,// = 0
+ output wire [4-1:0] M_AXI_ARCACHE,// = 0
+ output wire [3-1:0] M_AXI_ARPROT,// = 0
+ output wire [4-1:0] M_AXI_ARQOS,// = 0
+ //
+ // Simplified read data return channel
+ // input wire M_AXI_RVALID is assumed to be $past(ARVALID,1)
+ output wire M_AXI_RREADY,// = 1
+ input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [NS*2-1:0] M_AXI_RRESP
+ // input wire M_AXI_RLAST is assumed to be 1
+ // }}}
+ );
+
+ // Signal declarations
+ // {{{
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam IW = C_AXI_ID_WIDTH;
+ // LGNS is the number of bits required in a slave index
+ localparam LGNS = (NS <= 1) ? 1 : $clog2(NS);
+ //
+ localparam [1:0] OKAY = 2'b00,
+ EXOKAY = 2'b01,
+ SLVERR = 2'b10,
+ INTERCONNECT_ERROR = 2'b11;
+ // localparam ADDR_LSBS = $clog2(DW)-3;
+ //
+ reg locked_burst, locked_write, lock_valid;
+
+ // Write signals
+ // {{{
+ wire awskd_stall;
+ wire awskid_valid, bffull, bempty, write_awskidready,
+ dcd_awvalid;
+ reg write_bvalid, write_response;
+ reg bfull, write_no_index;
+ wire [NS:0] raw_wdecode;
+ reg [NS:0] last_wdecode, wdecode;
+ wire [AW-1:0] m_awaddr;
+ wire [LGNS-1:0] write_windex;
+ reg [LGNS-1:0] write_bindex;
+ wire [3-1:0] awskid_prot, m_axi_awprot;
+ wire [LGFLEN:0] bfill;
+ reg [LGFLEN:0] write_count;
+ reg [1:0] write_resp;
+ //
+ reg [C_AXI_ID_WIDTH-1:0] write_id, write_bid, write_retid;
+ reg [C_AXI_ADDR_WIDTH-1:0] write_addr;
+ wire [C_AXI_ID_WIDTH-1:0] awskid_awid;
+ wire [C_AXI_ADDR_WIDTH-1:0] awskid_awaddr, next_waddr;
+ reg write_request, write_topofburst,
+ write_beat_bvalid;
+ reg [3-1:0] write_size;
+ reg [2-1:0] write_burst;
+ reg [8-1:0] write_len, write_awlen;
+ wire [8-1:0] awskid_awlen;
+ wire [2-1:0] awskid_awburst;
+ wire [3-1:0] awskid_awsize;
+ wire awskid_awlock;
+ //
+ reg write_top_beat;
+ // }}}
+
+ // Read signals
+ // {{{
+ wire rempty, rdfull;
+ wire [LGFLEN:0] rfill;
+ wire [LGNS-1:0] read_index;
+ reg [LGNS-1:0] last_read_index;
+ reg [1:0] read_resp;
+ reg [DW-1:0] read_rdata;
+ wire read_rwait, arskd_stall;
+ reg read_rvalid, read_result, read_no_index;
+ wire [AW-1:0] m_araddr;
+ reg [AW-1:0] araddr;
+ reg [3-1:0] arprot;
+ wire [NS:0] raw_rdecode;
+
+ reg [C_AXI_ID_WIDTH-1:0] arid, read_rvid, read_retid;
+ reg [3-1:0] arsize;
+ reg [2-1:0] arburst;
+ reg arlock, read_rvlock;
+ reg read_rvlast, read_retlast;
+ reg [8-1:0] arlen, rlen;
+ wire [C_AXI_ADDR_WIDTH-1:0] next_araddr;
+ wire issue_read;
+ reg read_full;
+ reg [LGFLEN:0] read_count;
+ reg arvalid;
+ reg [NS:0] last_rdecode, rdecode;
+
+ wire [0:0] unused_pin;
+ // }}}
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Unused wire assignments
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ assign M_AXI_AWID = 0;
+ assign M_AXI_AWLEN = 0;
+ assign M_AXI_AWBURST = 2'b00;
+ assign M_AXI_AWLOCK = 1'b0;
+ assign M_AXI_AWCACHE = 4'h3;
+ // assign M_AXI_AWPROT = 3'h0;
+ assign M_AXI_AWQOS = 4'h0;
+ //
+ assign M_AXI_WVALID = M_AXI_AWVALID;
+ assign M_AXI_WLAST = 1'b1;
+ //
+ assign M_AXI_BREADY = 1'b1;
+ //
+ assign M_AXI_ARID = 1'b0;
+ assign M_AXI_ARLEN = 8'h0; // Burst of one beat
+ assign M_AXI_ARBURST = 2'b00; // INC
+ assign M_AXI_ARLOCK = 1'b0;
+ assign M_AXI_ARCACHE = 4'h3;
+ // assign M_AXI_ARPROT = 3'h0;
+ assign M_AXI_ARQOS = 4'h0;
+ //
+ assign M_AXI_RREADY = -1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic:
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Incoming write address requests must go through a skidbuffer. By
+ // keeping OPT_OUTREG == 0, this shouldn't cost us any time, but should
+ // instead buy us the ability to keep AWREADY high even if for reasons
+ // we can't act on AWVALID & AWREADY on the same cycle
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .DW(C_AXI_ID_WIDTH+AW+8+3+2+1+3)
+ // }}}
+ ) awskid(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID),
+ .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
+ S_AXI_AWSIZE, S_AXI_AWBURST,
+ S_AXI_AWLOCK, S_AXI_AWPROT }),
+ .o_valid(awskid_valid), .i_ready(write_awskidready),
+ .o_data({ awskid_awid, awskid_awaddr, awskid_awlen,
+ awskid_awsize, awskid_awburst, awskid_awlock,
+ awskid_prot })
+ // }}}
+ );
+
+ // write_addr and other write_*
+ // {{{
+ // On any write address request (post-skidbuffer), copy down the details
+ // of that request. Once these details are valid (i.e. on the next
+ // clock), S_AXI_WREADY will be true.
+ always @(posedge S_AXI_ACLK)
+ if (awskid_valid && write_awskidready)
+ begin
+ write_id <= awskid_awid;
+ write_addr <= awskid_awaddr;
+ write_size <= awskid_awsize;
+ write_awlen <= awskid_awlen;
+ write_burst <= awskid_awburst;
+ // write_lock <= awskid_awlock;
+
+ if (OPT_LOWPOWER && !awskid_valid)
+ begin
+ write_id <= {(C_AXI_ID_WIDTH){1'b0}};
+ write_addr <= {(C_AXI_ADDR_WIDTH){1'b0}};
+ write_size <= 3'h0;
+ write_awlen <= 8'h0;
+ write_burst <= 2'b00;
+ end
+ end else if (S_AXI_WVALID && S_AXI_WREADY)
+ // Following each write beat, we need to update our address
+ write_addr <= next_waddr;
+ // }}}
+
+ // next_waddr from get_next_write_address
+ // {{{
+ // Given the details of the address request, get the next address to
+ // write to.
+ axi_addr #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH)
+ // }}}
+ ) get_next_write_address(
+ // {{{
+ write_addr,
+ write_size, write_burst, write_awlen, next_waddr
+ // }}}
+ );
+ // }}}
+
+
+ // write_request, write_topofburst, write_len
+ // {{{
+ // Count through the beats of the burst in write_len. write_topofburst
+ // indicates the first beat in any new burst, but will be zero for all
+ // subsequent burst beats. write_request is true anytime we are trying
+ // to write.
+ initial write_request = 1'b0;
+ initial write_topofburst = 1'b1;
+ initial write_len = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ // {{{
+ write_request <= 1'b0;
+ write_topofburst <= 1'b1;
+ write_len <= 0;
+ // }}}
+ end else if (write_awskidready)
+ begin
+ // {{{
+ write_request <= awskid_valid;
+ write_topofburst <= awskid_valid;
+ write_len <= (awskid_valid) ? awskid_awlen : 8'h00;
+ // }}}
+ end else if (S_AXI_WVALID && S_AXI_WREADY)
+ begin
+ // {{{
+ write_topofburst <= 1'b0;
+ if (S_AXI_WLAST)
+ write_request <= 1'b0;
+ if (write_len > 0)
+ write_len <= write_len - 1;
+ // }}}
+ end
+ // }}}
+
+ // Slave address decoding
+ // {{{
+ // Decode our incoming address in order to determine the next
+ // slave the address addresses
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(3), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(1'b1)
+ // }}}
+ ) wraddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(awskid_valid && write_awskidready), .o_stall(awskd_stall),
+ // .i_addr(awskid_valid && write_awskidready
+ // ? awskid_awaddr : next_waddr),
+ .i_addr(awskid_awaddr),
+ .i_data(awskid_prot),
+ .o_valid(dcd_awvalid), .i_stall(!S_AXI_WVALID),
+ .o_decode(raw_wdecode), .o_addr(m_awaddr),
+ .o_data(m_axi_awprot)
+ // }}}
+ );
+ // }}}
+
+ // last_wdecode
+ // {{{
+ // We only do our decode on the address request. We need the decoded
+ // values long after the top of the burst. Therefore, let's use
+ // dcd_awvalid to know we have a valid output from the decoder and
+ // then we'll latch that (register it really) for the rest of the burst
+ always @(posedge S_AXI_ACLK)
+ if (dcd_awvalid)
+ last_wdecode <= raw_wdecode;
+ // }}}
+
+ // wdecode
+ // {{{
+ always @(*)
+ begin
+ if (dcd_awvalid)
+ wdecode = raw_wdecode;
+ else
+ wdecode = last_wdecode;
+ end
+ // }}}
+
+ // Downstream slave (write) signals
+ // {{{
+ // It's now time to create our write request for the slave. Slave
+ // writes take place on the clock after address valid is true as long
+ // as S_AXI_WVALID is true. This places combinatorial logic onto the
+ // outgoing AWVALID. The sign that we are in the middle of a burst
+ // will specifically be that WREADY is true.
+ //
+
+ //
+ // If there were any part of this algorithm I disliked it would be the
+ // AWVALID logic here. It shouldn't nearly be this loaded.
+ assign S_AXI_WREADY = write_request;
+ assign M_AXI_AWVALID = (S_AXI_WVALID && write_request
+ && (!locked_burst || locked_write)) ? wdecode[NS-1:0] : 0;
+ assign M_AXI_AWADDR = write_addr;
+ assign M_AXI_AWPROT = m_axi_awprot;
+ assign M_AXI_AWSIZE = write_size;
+ assign M_AXI_WDATA = S_AXI_WDATA;
+ assign M_AXI_WSTRB = S_AXI_WSTRB;
+ // }}}
+
+ // write_awskidready
+ // {{{
+ // We can accept a new value from the skid buffer as soon as the last
+ // write value comes in, or equivalently if we are not in the middle
+ // of a write. This is all subject, of course, to our backpressure
+ // FIFO not being full.
+ assign write_awskidready = ((S_AXI_WVALID&&S_AXI_WLAST)
+ || !S_AXI_WREADY) && !bfull;
+ // }}}
+
+ // write_windex
+ // {{{
+ // Back out an index from our decoded slave value
+ generate if (NS <= 1)
+ begin : WR_ONE_SLAVE
+
+ assign write_windex = 0;
+
+ end else begin : WR_INDEX
+ reg [LGNS-1:0] r_write_windex;
+ integer k;
+
+ always @(*)
+ begin
+ r_write_windex = 0;
+ for(k=0; k<NS; k=k+1)
+ if (wdecode[k])
+ r_write_windex = r_write_windex | k[LGNS-1:0];
+ end
+
+ assign write_windex = r_write_windex;
+ end endgenerate
+ // }}}
+
+ always @(posedge S_AXI_ACLK)
+ begin
+ write_bindex <= write_windex;
+ write_no_index <= wdecode[NS];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ // if (write_top_of_burst) // -- not necessary
+ write_bid <= write_id;
+
+ // write_response, write_bvalid
+ // {{{
+ // write_bvalid will be true one clock after the last write is accepted.
+ // This is the internal signal that would've come from a subordinate
+ // slave's BVALID, save that we are generating it internally.
+ initial { write_response, write_bvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { write_response, write_bvalid } <= 0;
+ else
+ { write_response, write_bvalid } <= { write_bvalid,
+ (S_AXI_WVALID && S_AXI_WREADY&& S_AXI_WLAST) };
+ // }}}
+
+ // write_top_beat, write_beat_bvalid
+ // {{{
+ // Examine write beats, not just write bursts
+ always @(posedge S_AXI_ACLK)
+ begin
+ write_top_beat <= write_topofburst;
+ write_beat_bvalid <= S_AXI_WVALID && S_AXI_WREADY;
+ end
+ // }}}
+
+ // write_resp
+ // {{{
+ // The response from any burst should be an DECERR (interconnect
+ // error) if ever the addressed slave doesn't exist in our address map.
+ // This is sticky: any attempt to generate a request to a non-existent
+ // slave will generate an interconnect error. Likewise, if the slave
+ // ever returns a slave error, we'll propagate it back in the burst
+ // return. Finally, on an exclusive access burst, we'll return EXOKAY
+ // if we could write the values.
+ always @(posedge S_AXI_ACLK)
+ if (write_beat_bvalid)
+ begin
+ if (write_no_index)
+ write_resp <= INTERCONNECT_ERROR;
+ else if (M_AXI_BRESP[2*write_bindex])
+ write_resp <= { 1'b1, (write_top_beat)
+ ? 1'b0 : write_resp[0] };
+ else if (write_top_beat || !write_resp[1])
+ write_resp <= { 1'b0, (write_top_beat && locked_burst && locked_write) };
+ end else if (OPT_LOWPOWER)
+ write_resp <= 2'b00;
+ // }}}
+
+ // write_retid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ write_retid <= write_bid;
+ // }}}
+
+ // write_count and bfull -- pseudo FIFO counters
+ // {{{
+ // The pseudo-FIFO for the write side. This counter will let us know
+ // if any write response will ever overflow our write response FIFO,
+ // allowing us to be able to confidently deal with any backpressure.
+ initial write_count = 0;
+ initial bfull = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ write_count <= 0;
+ bfull <= 0;
+ end else case({ (awskid_valid && write_awskidready),
+ (S_AXI_BVALID & S_AXI_BREADY) })
+ 2'b01: begin
+ write_count <= write_count - 1;
+ bfull <= 1'b0;
+ end
+ 2'b10: begin
+ write_count <= write_count + 1;
+ bfull <= (&write_count[LGFLEN-1:0]);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // Backpressure FIFO on write response returns
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(C_AXI_ID_WIDTH+2),
+ .OPT_ASYNC_READ(0),
+ .LGFLEN(LGFLEN)
+ // }}}
+ ) bfifo (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(write_response), .i_data({ write_retid, write_resp }),
+ .o_full(bffull), .o_fill(bfill),
+ .i_rd(S_AXI_BVALID && S_AXI_BREADY),
+ .o_data({ S_AXI_BID, S_AXI_BRESP }),
+ .o_empty(bempty)
+ // }}}
+ );
+ // }}}
+
+ assign S_AXI_BVALID = !bempty;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // ar*
+ // {{{
+ // Copy the burst information, for use in determining the next address
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ // {{{
+ araddr <= S_AXI_ARADDR;
+ arid <= S_AXI_ARID;
+ arlen <= S_AXI_ARLEN;
+ arsize <= S_AXI_ARSIZE;
+ arburst <= S_AXI_ARBURST;
+ arlock <= S_AXI_ARLOCK && S_AXI_ARBURST == 2'b01;
+ arprot <= S_AXI_ARPROT;
+ // }}}
+ end else if (issue_read)
+ araddr <= next_araddr;
+ // }}}
+
+ // rlen
+ // {{{
+ // Count the number of remaining items in a burst. Note that rlen
+ // counts from N-1 to 0, not from N to 1.
+ initial rlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rlen <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ rlen <= S_AXI_ARLEN;
+ else if (issue_read && (rlen > 0))
+ rlen <= rlen - 1;
+ // }}}
+
+ // arvalid
+ // {{{
+ // Should the slave M_AXI_ARVALID be true in general? Based upon
+ // rlen above, but still needs to be gated across all slaves.
+ initial arvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ arvalid <= 1'b0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ arvalid <= 1'b1;
+ else if (issue_read && (rlen == 0))
+ arvalid <= 1'b0;
+ // }}}
+
+ // next_araddr -- Get the next AXI address
+ // {{{
+ axi_addr #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH)
+ // }}}
+ ) get_next_read_address(
+ // {{{
+ araddr,
+ arsize, arburst, arlen, next_araddr
+ // }}}
+ );
+ // }}}
+
+ // raw_rdecode-- Decode which slave is being addressed by this read.
+ // {{{
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(1), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(1'b1)
+ // }}}
+ ) rdaddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID && S_AXI_ARREADY || rlen>0),
+ // Warning: there's no skid on this stall
+ .o_stall(arskd_stall),
+ .i_addr((S_AXI_ARVALID & S_AXI_ARREADY)
+ ? S_AXI_ARADDR : next_araddr),
+ .i_data(1'b0),
+ .o_valid(read_rwait), .i_stall(!issue_read),
+ .o_decode(raw_rdecode), .o_addr(m_araddr),
+ .o_data(unused_pin[0])
+ // }}}
+ );
+ // }}}
+
+ // last_rdecode
+ // {{{
+ // We want the value from the decoder on the first clock cycle. It
+ // may not be valid after that, so we'll hold on to it in last_rdecode
+ initial last_rdecode = 0;
+ always @(posedge S_AXI_ACLK)
+ if (read_rwait)
+ last_rdecode <= raw_rdecode;
+ // }}}
+
+ // rdecode
+ // {{{
+ always @(*)
+ if (read_rwait)
+ rdecode = raw_rdecode;
+ else
+ rdecode = last_rdecode;
+ // }}}
+
+ // Finally, issue our read request any time the FIFO isn't full
+ // {{{
+ assign issue_read = !read_full;
+
+ assign M_AXI_ARVALID = issue_read ? rdecode[NS-1:0] : 0;
+ assign M_AXI_ARADDR = m_araddr;
+ assign M_AXI_ARPROT = arprot;
+ assign M_AXI_ARSIZE = arsize;
+ // }}}
+
+ // read_rvalid, read_result
+ // {{{
+ // read_rvalid would be the RVALID response from the slave that would
+ // be returned if we checked it. read_result is the same thing--one
+ // clock later.
+ initial { read_result, read_rvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_result, read_rvalid } <= 2'b00;
+ else
+ { read_result, read_rvalid } <= { read_rvalid,
+ (arvalid&issue_read) };
+ // }}}
+
+ // read_rvid, read_rvlast, read_rvlock
+ // {{{
+ // On the same clock when rvalid is true, we'll also want to know
+ // if RLAST should be true (decoded here, not in the slave), and
+ // whether or not the transaction is locked. These values are valid
+ // any time read_rvalid is true.
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (arvalid && issue_read)
+ begin
+ read_rvid <= arid;
+ read_rvlast <= (rlen == 0);
+ read_rvlock <= (read_rvlock && !read_rvlast) || (OPT_EXCLUSIVE_ACCESS && arlock && lock_valid);
+ end else if (read_rvlast)
+ read_rvlock <= 1'b0;
+
+ if (!S_AXI_ARESETN)
+ begin
+ read_rvlock <= 1'b0;
+ read_rvlast <= 1'b1;
+ end
+ end
+ // }}}
+
+ // read_retid, read_retlast
+ // {{{
+ // read_result is true one clock after read_rvalid is true. Copy
+ // the ID and LAST values into this pipeline clock cycle
+ always @(posedge S_AXI_ACLK)
+ begin
+ read_retid <= read_rvid;
+ read_retlast <= read_rvlast;
+ end
+ // }}}
+
+ //
+ // Decode the read value.
+ //
+
+ // read_index - First step is to calculate the index of the slave
+ // {{{
+ generate if (NS <= 1)
+ begin : RD_ONE_SLAVE
+
+ assign read_index = 0;
+
+ end else begin : RD_INDEX
+ reg [LGNS-1:0] r_read_index = 0;
+ integer k;
+
+ always @(*)
+ begin
+ r_read_index = 0;
+
+ for(k=0; k<NS; k=k+1)
+ if (rdecode[k])
+ r_read_index = r_read_index | k[LGNS-1:0];
+ end
+
+ assign read_index = r_read_index;
+ end endgenerate
+ // }}}
+
+ // last_read_index
+ // {{{
+ // Keep this index into the RVALID cycle
+ always @(posedge S_AXI_ACLK)
+ last_read_index <= read_index;
+ // }}}
+
+ // read_no_index is a flag to indicate that no slave was indexed.
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ read_no_index <= rdecode[NS];
+ // }}}
+
+ // read_rdata
+ // {{{
+ // Now we can use last_read_index to determine the return data.
+ // read_rdata will be valid on the same clock $past(RVALID) or
+ // read_return cycle
+ always @(posedge S_AXI_ACLK)
+ read_rdata <= M_AXI_RDATA[DW*last_read_index +: DW];
+ // }}}
+
+ // read_resp
+ // {{{
+ // As with read_rdata, read_resp is the response from the slave
+ always @(posedge S_AXI_ACLK)
+ if (read_no_index)
+ read_resp <= INTERCONNECT_ERROR;
+ else if (M_AXI_RRESP[2*last_read_index + 1])
+ read_resp <= SLVERR; // SLVERR
+ else if (OPT_EXCLUSIVE_ACCESS && read_rvlock)
+ read_resp <= EXOKAY; // Exclusive access Okay
+ else
+ read_resp <= OKAY; // OKAY
+ // }}}
+
+ // read_count, read_full
+ // {{{
+ // Since we can't allow the incoming requests to overflow in the
+ // presence of any back pressure, let's create a phantom FIFO here
+ // counting the number of values either in the final pipeline or in
+ // final read FIFO. If read_full is true, the FIFO is full and we
+ // cannot move any more data forward.
+ initial { read_count, read_full } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_count, read_full } <= 0;
+ else case({ read_rwait && issue_read, S_AXI_RVALID & S_AXI_RREADY})
+ 2'b10: begin
+ read_count <= read_count + 1;
+ read_full <= &read_count[LGFLEN-1:0];
+ end
+ 2'b01: begin
+ read_count <= read_count - 1;
+ read_full <= 1'b0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ assign S_AXI_ARREADY = (rlen == 0) && !read_full;
+
+ // Read return FIFO for dealing with backpressure
+ // {{{
+ // Send the return results through a synchronous FIFO to handle
+ // back-pressure. Doing this costs us one clock of latency.
+ sfifo #(
+ // {{{
+ .BW(C_AXI_ID_WIDTH+DW+1+2),
+ .OPT_ASYNC_READ(0), .LGFLEN(LGFLEN)
+ // }}}
+ ) rfifo (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(read_result), .i_data({ read_retid, read_rdata,
+ read_retlast, read_resp }),
+ .o_full(rdfull), .o_fill(rfill),
+ .i_rd(S_AXI_RVALID && S_AXI_RREADY),
+ .o_data({ S_AXI_RID, S_AXI_RDATA,
+ S_AXI_RLAST, S_AXI_RRESP }),
+ .o_empty(rempty)
+ // }}}
+ );
+ // }}}
+
+ assign S_AXI_RVALID = !rempty;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Exclusive access / Bus locking logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_EXCLUSIVE_ACCESS)
+ begin : EXCLUSIVE_ACCESS
+ // {{{
+ reg r_lock_valid, r_locked_burst;
+ reg [AW-1:0] lock_addr, lock_last;
+ reg [4-1:0] lock_len;
+ reg [3-1:0] lock_size;
+ reg [IW-1:0] lock_id;
+
+ initial r_lock_valid = 1'b0;
+ initial r_locked_burst = 1'b0;
+ initial locked_write = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ begin
+
+ //
+ // Step one: Set the lock_valid signal. This means
+ // that a read request has been successful requesting
+ // the lock for this address.
+ //
+ if (awskid_valid && write_awskidready)
+ begin
+ // On any write to the value inside our lock
+ // range, disable the lock_valid signal
+ if ((awskid_awaddr
+ + ({ {(AW-4){1'b0}},awskid_awlen[3:0]} << S_AXI_AWSIZE)
+ >= lock_addr)
+ &&(S_AXI_AWADDR <= lock_last))
+ r_lock_valid <= 0;
+ end
+
+ if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLOCK
+ && S_AXI_ARBURST == 2'b01)
+ begin
+ r_lock_valid <= !locked_write;
+ lock_addr <= S_AXI_ARADDR;
+ lock_id <= S_AXI_ARID;
+ lock_size <= S_AXI_ARSIZE;
+ lock_len <= S_AXI_ARLEN[3:0];
+ lock_last <= S_AXI_ARADDR
+ + ({ {(AW-4){1'b0}}, lock_len }
+ << S_AXI_ARSIZE);
+ end
+
+ if (awskid_valid && write_awskidready)
+ begin
+ r_locked_burst <= 1'b0;
+ locked_write <= awskid_awlock;
+
+ if (awskid_awlock)
+ begin
+ r_locked_burst <= r_lock_valid;
+ if (lock_addr != awskid_awaddr)
+ r_locked_burst <= 1'b0;
+ if (lock_id != awskid_awid)
+ r_locked_burst <= 1'b0;
+ if (lock_size != awskid_awsize)
+ r_locked_burst <= 1'b0;
+ if (lock_len != awskid_awlen[3:0])
+ r_locked_burst <= 1'b0;
+ if (2'b01 != awskid_awburst)
+ r_locked_burst <= 1'b0;
+ end
+
+ // Write if !locked_write || write_burst
+ // EXOKAY on locked_write && write_burst
+ // OKAY on all other writes where the slave
+ // does not assert an error
+ end else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
+ r_locked_burst <= 1'b0;
+
+ if (!S_AXI_ARESETN)
+ begin
+ r_lock_valid <= 1'b0;
+ r_locked_burst <= 1'b0;
+ end
+ end
+
+ assign locked_burst = r_locked_burst;
+ assign lock_valid = r_lock_valid;
+ // }}}
+ end else begin : NO_EXCLUSIVE_ACCESS
+ // {{{
+ // Keep track of whether or not the current burst requests
+ // exclusive access or not. locked_write is an important
+ // signal used to make certain that we do not write to our
+ // slave on any locked write requests. (Shouldn't happen,
+ // since we aren't returning any EXOKAY's from reads ...)
+ always @(posedge S_AXI_ACLK)
+ if (awskid_valid && write_awskidready)
+ locked_write <= awskid_awlock;
+ else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
+ locked_write <= 1'b0;
+
+ assign locked_burst = 0;
+ assign lock_valid = 0;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+ S_AXI_AWCACHE, S_AXI_ARCACHE,
+ S_AXI_AWQOS, S_AXI_ARQOS,
+ dcd_awvalid, m_awaddr, unused_pin,
+ bffull, rdfull, bfill, rfill,
+ awskd_stall, arskd_stall };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal verification properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGFLEN+9;
+
+ //
+ // ...
+ //
+
+ faxi_slave #( .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .F_AXI_MAXDELAY(5),
+ .F_LGDEPTH(F_LGDEPTH))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awid( S_AXI_AWID),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awlen( S_AXI_AWLEN),
+ .i_axi_awsize( S_AXI_AWSIZE),
+ .i_axi_awburst(S_AXI_AWBURST),
+ .i_axi_awlock( S_AXI_AWLOCK),
+ .i_axi_awcache(S_AXI_AWCACHE),
+ .i_axi_awprot( S_AXI_AWPROT),
+ .i_axi_awqos( S_AXI_AWQOS),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ .i_axi_wlast( S_AXI_WLAST),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_arid( S_AXI_ARID),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arlen( S_AXI_ARLEN),
+ .i_axi_arsize( S_AXI_ARSIZE),
+ .i_axi_arburst(S_AXI_ARBURST),
+ .i_axi_arlock( S_AXI_ARLOCK),
+ .i_axi_arcache(S_AXI_ARCACHE),
+ .i_axi_arprot( S_AXI_ARPROT),
+ .i_axi_arqos( S_AXI_ARQOS),
+ //
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rid( S_AXI_RID),
+ .i_axi_rdata( S_AXI_RDATA),
+ .i_axi_rlast( S_AXI_RLAST),
+ .i_axi_rresp( S_AXI_RRESP)
+ //
+ // ...
+ //
+ );
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (!OPT_EXCLUSIVE_ACCESS)
+ begin
+ assert(!S_AXI_BVALID || S_AXI_BRESP != EXOKAY);
+ assert(!S_AXI_RVALID || S_AXI_RRESP != EXOKAY);
+ end
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Properties necessary to pass induction
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ assert($onehot0(M_AXI_AWVALID));
+
+ always @(*)
+ assert($onehot0(M_AXI_ARVALID));
+
+ //
+ //
+ // Write properties
+ //
+ //
+
+ always @(*)
+ if (S_AXI_WVALID && S_AXI_WREADY)
+ begin
+ if (locked_burst && !locked_write)
+ assert(M_AXI_AWVALID == 0);
+ else if (wdecode[NS])
+ assert(M_AXI_AWVALID == 0);
+ else begin
+ assert($onehot(M_AXI_AWVALID));
+ assert(M_AXI_AWVALID == wdecode[NS-1:0]);
+ end
+ end else
+ assert(M_AXI_AWVALID == 0);
+
+ //
+ // ...
+ //
+
+ //
+ //
+ // Read properties
+ //
+ //
+
+ //
+ // ...
+ //
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Simplifying (careless) assumptions
+ //
+ // Caution: these might void your proof
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam [0:0] F_CHECK_WRITES = 1'b1;
+ localparam [0:0] F_CHECK_READS = 1'b1;
+
+ generate if (!F_CHECK_WRITES)
+ begin
+ always @(*)
+ assume(!S_AXI_AWVALID);
+ always @(*)
+ assert(!S_AXI_BVALID);
+ always @(*)
+ assert(!M_AXI_AWVALID);
+
+ // ...
+ end endgenerate
+
+ generate if (!F_CHECK_READS)
+ begin
+ always @(*)
+ assume(!S_AXI_ARVALID);
+ always @(*)
+ assert(!S_AXI_RVALID);
+ always @(*)
+ assert(M_AXI_ARVALID == 0);
+ always @(*)
+ assert(rdecode == 0);
+ // ...
+ end endgenerate
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [3:0] cvr_arvalids, cvr_awvalids, cvr_reads, cvr_writes;
+ (* anyconst *) reg cvr_burst;
+
+ always @(*)
+ if (cvr_burst && S_AXI_AWVALID)
+ assume(S_AXI_AWLEN > 2);
+
+ always @(*)
+ if (cvr_burst && S_AXI_ARVALID)
+ assume(S_AXI_ARLEN > 2);
+
+ initial cvr_awvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!cvr_burst || !S_AXI_ARESETN)
+ cvr_awvalids <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && !(&cvr_awvalids))
+ cvr_awvalids <= cvr_awvalids + 1;
+
+ initial cvr_arvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!cvr_burst || !S_AXI_ARESETN)
+ cvr_arvalids <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && !(&cvr_arvalids))
+ cvr_arvalids <= cvr_arvalids + 1;
+
+ initial cvr_writes = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!cvr_burst || !S_AXI_ARESETN)
+ cvr_writes <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY && !(&cvr_writes))
+ cvr_writes <= cvr_writes + 1;
+
+ initial cvr_reads = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_reads <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
+ && !(&cvr_arvalids))
+ cvr_reads <= cvr_reads + 1;
+
+ generate if (F_CHECK_WRITES)
+ begin : COVER_WRITES
+
+ always @(*)
+ cover(cvr_awvalids > 2);
+
+ always @(*)
+ cover(cvr_writes > 2);
+
+ always @(*)
+ cover(cvr_writes > 4);
+ end endgenerate
+
+ generate if (F_CHECK_READS)
+ begin : COVER_READS
+ always @(*)
+ cover(cvr_arvalids > 2);
+
+ always @(*)
+ cover(cvr_reads > 2);
+
+ always @(*)
+ cover(cvr_reads > 4);
+ end endgenerate
+
+ always @(*)
+ cover((cvr_writes > 2) && (cvr_reads > 2));
+
+ generate if (OPT_EXCLUSIVE_ACCESS)
+ begin : COVER_EXCLUSIVE_ACCESS
+
+ always @(*)
+ cover(S_AXI_BVALID && S_AXI_BRESP == EXOKAY);
+
+ end endgenerate
+`endif
+endmodule
diff --git a/rtl/wb2axip/axiempty.v b/rtl/wb2axip/axiempty.v
new file mode 100644
index 0000000..d0ec896
--- /dev/null
+++ b/rtl/wb2axip/axiempty.v
@@ -0,0 +1,490 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axiempty.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A basic AXI core to provide a response to an AXI master when
+// no other slaves are connected to the bus. All results are
+// bus errors.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axiempty #(
+ // {{{
+ parameter integer C_AXI_ID_WIDTH = 2,
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ // Verilator lint_off UNUSED
+ parameter integer C_AXI_ADDR_WIDTH = 6,
+ // Verilator lint_on UNUSED
+ parameter [0:0] OPT_LOWPOWER = 0
+ // Some useful short-hand definitions
+ // localparam AW = C_AXI_ADDR_WIDTH,
+ // localparam DW = C_AXI_DATA_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire S_AXI_WLAST,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [7:0] S_AXI_ARLEN,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire S_AXI_RLAST,
+ output wire [1:0] S_AXI_RRESP
+ // }}}
+ );
+
+ localparam IW = C_AXI_ID_WIDTH;
+ // Double buffer the write response channel only
+ reg [IW-1 : 0] axi_bid;
+ reg axi_bvalid;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Start with the two skid buffers
+ // {{{
+ wire m_awvalid, m_wvalid;
+ wire m_awready, m_wready, m_wlast;
+ wire [IW-1:0] m_awid;
+ //
+ skidbuffer #(.DW(IW), .OPT_OUTREG(1'b0))
+ awskd(S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_AWVALID, S_AXI_AWREADY, S_AXI_AWID,
+ m_awvalid, m_awready, m_awid );
+
+ skidbuffer #(.DW(1), .OPT_OUTREG(1'b0))
+ wskd(S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_WVALID, S_AXI_WREADY, S_AXI_WLAST,
+ m_wvalid, m_wready, m_wlast );
+ // }}}
+
+ // m_awready, m_wready
+ // {{{
+ // The logic here is pretty simple--accept a write address burst
+ // into the skid buffer, then leave it there while the write data comes
+ // on. Once we get to the last write data element, accept both it and
+ // the address. This spares us the trouble of counting out the elements
+ // in the write burst.
+ //
+ assign m_awready= (m_awvalid && m_wvalid && m_wlast)
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+ assign m_wready = !m_wlast || m_awready;
+ // }}}
+
+ // bvalid
+ // {{{
+ // As soon as m_awready above, a packet has come through successfully.
+ // Acknowledge it with a bus error.
+ //
+ initial axi_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_bvalid <= 1'b0;
+ else if (m_awready)
+ axi_bvalid <= 1'b1;
+ else if (S_AXI_BREADY)
+ axi_bvalid <= 1'b0;
+ // }}}
+
+ // bid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (m_awready)
+ axi_bid <= m_awid;
+ // }}}
+
+ assign S_AXI_BVALID = axi_bvalid;
+ assign S_AXI_BID = axi_bid;
+ assign S_AXI_BRESP = 2'b11; // An interconnect bus error
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read half
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [IW-1:0] rid, axi_rid;
+ reg axi_arready, axi_rlast, axi_rvalid;
+ reg [8:0] axi_rlen;
+
+ // axi_arready
+ // {{{
+ initial axi_arready = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arready <= 1;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_arready <= (S_AXI_ARLEN==0)&&(!S_AXI_RVALID|| S_AXI_RREADY);
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if ((!axi_arready)&&(S_AXI_RVALID))
+ axi_arready <= (axi_rlen <= 2);
+ end
+ // }}}
+
+ // axi_rlen
+ // {{{
+ initial axi_rlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_rlen <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_rlen <= (S_AXI_ARLEN+1)
+ + ((S_AXI_RVALID && !S_AXI_RREADY) ? 1:0);
+ else if (S_AXI_RREADY && S_AXI_RVALID)
+ axi_rlen <= axi_rlen - 1;
+ // }}}
+
+ // rid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ rid <= 0;
+ else if (S_AXI_ARREADY && (!OPT_LOWPOWER || S_AXI_ARVALID))
+ rid <= S_AXI_ARID;
+ else if (OPT_LOWPOWER && S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ rid <= 0;
+ // }}}
+
+ // axi_rvalid
+ // {{{
+ initial axi_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_rvalid <= 0;
+ else if (S_AXI_ARVALID || (axi_rlen > 1))
+ axi_rvalid <= 1;
+ else if (S_AXI_RREADY)
+ axi_rvalid <= 0;
+ // }}}
+
+ // axi_rid
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ axi_rid <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_rid <= S_AXI_ARID;
+ else if (OPT_LOWPOWER && S_AXI_RVALID && S_AXI_RREADY
+ && S_AXI_RLAST)
+ axi_rid <= 0;
+ else
+ axi_rid <= rid;
+ end
+ // }}}
+
+ // axi_rlast
+ // {{{
+ initial axi_rlast = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ axi_rlast <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_rlast <= (S_AXI_ARLEN == 0);
+ else if (S_AXI_RVALID)
+ axi_rlast <= (axi_rlen == 2);
+ else
+ axi_rlast <= (axi_rlen == 1);
+ end
+ // }}}
+
+ //
+ assign S_AXI_ARREADY = axi_arready;
+ assign S_AXI_RVALID = axi_rvalid;
+ assign S_AXI_RID = axi_rid;
+ assign S_AXI_RDATA = 0;
+ assign S_AXI_RRESP = 2'b11;
+ assign S_AXI_RLAST = axi_rlast;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The following properties are only some of the properties used
+ // to verify this core
+ //
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH)
+ // }}}
+ f_slave(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awid( S_AXI_AWID),
+ .i_axi_awaddr( {(C_AXI_ADDR_WIDTH){1'b0}}),
+ .i_axi_awlen( S_AXI_AWLEN),
+ .i_axi_awsize( LSB[2:0]),
+ .i_axi_awburst(2'b0),
+ .i_axi_awlock( 1'b0),
+ .i_axi_awcache(4'h0),
+ .i_axi_awprot( 3'h0),
+ .i_axi_awqos( 4'h0),
+ // }}}
+ // Write Data Channel
+ // {{{
+ // Write Data
+ .i_axi_wdata({(C_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_wstrb({(C_AXI_DATA_WIDTH/8){1'b0}}),
+ .i_axi_wlast(S_AXI_WLAST),
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ // }}}
+ // Write response
+ // {{{
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_arid( S_AXI_ARID),
+ .i_axi_araddr( {(C_AXI_ADDR_WIDTH){1'b0}}),
+ .i_axi_arlen( S_AXI_ARLEN),
+ .i_axi_arsize( LSB[2:0]),
+ .i_axi_arburst(2'b00),
+ .i_axi_arlock( 1'b0),
+ .i_axi_arcache(4'h0),
+ .i_axi_arprot( 3'h0),
+ .i_axi_arqos( 4'h0),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rid(S_AXI_RID),
+ .i_axi_rdata(S_AXI_RDATA),
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rlast(S_AXI_RLAST),
+ //
+ // ...
+ // }}}
+ );
+
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write induction properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read induction properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $rose(S_AXI_RLAST))
+ assert(S_AXI_ARREADY);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checking
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ reg f_wr_cvr_valid, f_rd_cvr_valid;
+
+ initial f_wr_cvr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_wr_cvr_valid <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN > 4)
+ f_wr_cvr_valid <= 1;
+
+ always @(*)
+ cover(!S_AXI_BVALID && axi_awready && !m_awvalid
+ && f_wr_cvr_valid /* && ... */));
+
+ initial f_rd_cvr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_rd_cvr_valid <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 4)
+ f_rd_cvr_valid <= 1;
+
+ always @(*)
+ cover(S_AXI_ARREADY && f_rd_cvr_valid /* && ... */);
+
+ //
+ // Generate cover statements associated with multiple successive bursts
+ //
+ // These will be useful for demonstrating the throughput of the core.
+ //
+ reg [4:0] f_dbl_rd_count, f_dbl_wr_count;
+
+ initial f_dbl_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dbl_wr_count = 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN == 3)
+ begin
+ if (!(&f_dbl_wr_count))
+ f_dbl_wr_count <= f_dbl_wr_count + 1;
+ end
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 1)); //!
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 3)); //!
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 3) && !m_awvalid
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID && !S_AXI_BVALID)
+ && (f_axi_awr_nbursts == 0)
+ && (f_axi_wr_pending == 0)); //!!
+
+ initial f_dbl_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dbl_rd_count = 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 3)
+ begin
+ if (!(&f_dbl_rd_count))
+ f_dbl_rd_count <= f_dbl_rd_count + 1;
+ end
+
+ always @(*)
+ cover(!S_AXI_ARESETN && (f_dbl_rd_count > 3)
+ /* && ... */
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions necessary to pass a formal check
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // No limiting assumptions at present, check is currently full and
+ // complete
+ //
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axil2apb.v b/rtl/wb2axip/axil2apb.v
new file mode 100644
index 0000000..d8a73ae
--- /dev/null
+++ b/rtl/wb2axip/axil2apb.v
@@ -0,0 +1,717 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axil2apb.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: High throughput AXI-lite bridge to APB. With both skid
+// buffers enabled, it can handle 50% throughput--the maximum
+// that APB can handle.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axil2apb #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ // OPT_OUTGOING_SKIDBUFFER: required for 50% throughput
+ parameter [0:0] OPT_OUTGOING_SKIDBUFFER = 1'b0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI-lite interface
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
+ input wire [(C_AXI_DATA_WIDTH/8)-1:0] S_AXI_WSTRB,
+ //
+ output reg S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output reg [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output reg S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output reg [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output reg [1:0] S_AXI_RRESP,
+ // }}}
+ //
+ // The APB interface
+ // {{{
+ output reg M_APB_PSEL,
+ output reg M_APB_PENABLE,
+ input wire M_APB_PREADY,
+ output reg [C_AXI_ADDR_WIDTH-1:0] M_APB_PADDR,
+ output reg M_APB_PWRITE,
+ output reg [C_AXI_DATA_WIDTH-1:0] M_APB_PWDATA,
+ output reg [C_AXI_DATA_WIDTH/8-1:0] M_APB_PWSTRB,
+ output reg [2:0] M_APB_PPROT,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_APB_PRDATA,
+ input wire M_APB_PSLVERR
+ // }}}
+ // }}}
+ );
+
+ // Register declarations
+ // {{{
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam AXILLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ wire awskd_valid, wskd_valid, arskd_valid;
+ reg axil_write_ready, axil_read_ready,
+ write_grant, apb_idle;
+ wire [AW-AXILLSB-1:0] awskd_addr, arskd_addr;
+ wire [DW-1:0] wskd_data;
+ wire [DW/8-1:0] wskd_strb;
+ wire [2:0] awskd_prot, arskd_prot;
+ reg apb_bvalid, apb_rvalid, apb_error, out_skid_full;
+ reg [DW-1:0] apb_data;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming AXI-lite write interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // awskd - write address skid buffer
+ // {{{
+ skidbuffer #(.DW(C_AXI_ADDR_WIDTH-AXILLSB + 3),
+ .OPT_OUTREG(0)
+ ) awskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWADDR[AW-1:AXILLSB], S_AXI_AWPROT }),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data({ awskd_addr, awskd_prot }));
+ // }}}
+
+ // wskd - write data skid buffer
+ // {{{
+ skidbuffer #(.DW(DW+(DW/8)),
+ .OPT_OUTREG(0)
+ ) wskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+ // }}}
+
+ // apb_idle
+ // {{{
+ always @(*)
+ begin
+ apb_idle = !M_APB_PSEL;// || (M_APB_PENABLE && M_APB_PREADY);
+ if (OPT_OUTGOING_SKIDBUFFER && (M_APB_PENABLE && M_APB_PREADY))
+ apb_idle = 1'b1;
+ end
+ // }}}
+
+ // axil_write_ready
+ // {{{
+ always @(*)
+ begin
+ axil_write_ready = apb_idle;
+ if (S_AXI_BVALID && !S_AXI_BREADY)
+ axil_write_ready = 1'b0;
+ if (!awskd_valid || !wskd_valid)
+ axil_write_ready = 1'b0;
+ if (!write_grant && arskd_valid)
+ axil_write_ready = 1'b0;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming AXI-lite read interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // arskd buffer
+ // {{{
+ skidbuffer #(.DW(C_AXI_ADDR_WIDTH-AXILLSB+3),
+ .OPT_OUTREG(0)
+ ) arskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data({ S_AXI_ARADDR[AW-1:AXILLSB], S_AXI_ARPROT }),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data({ arskd_addr, arskd_prot }));
+ // }}}
+
+ // axil_read_ready
+ // {{{
+ always @(*)
+ begin
+ axil_read_ready = apb_idle;
+ if (S_AXI_RVALID && !S_AXI_RREADY)
+ axil_read_ready = 1'b0;
+ if (write_grant && awskd_valid && wskd_valid)
+ axil_read_ready = 1'b0;
+ if (!arskd_valid)
+ axil_read_ready = 1'b0;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Arbitrate among reads and writes --- alternating arbitration
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // write_grant -- alternates
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (apb_idle)
+ begin
+ if (axil_write_ready)
+ write_grant <= 1'b0;
+ else if (axil_read_ready)
+ write_grant <= 1'b1;
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Drive the APB bus
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // APB bus
+ // {{{
+ initial M_APB_PSEL = 1'b0;
+ initial M_APB_PENABLE = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (apb_idle)
+ begin
+ M_APB_PSEL <= 1'b0;
+ if (axil_read_ready)
+ begin
+ M_APB_PSEL <= 1'b1;
+ M_APB_PADDR <= { arskd_addr, {(AXILLSB){1'b0}} };
+ M_APB_PWRITE <= 1'b0;
+ M_APB_PPROT <= arskd_prot;
+ end else if (axil_write_ready)
+ begin
+ M_APB_PSEL <= 1'b1;
+ M_APB_PADDR <= { awskd_addr, {(AXILLSB){1'b0}} };
+ M_APB_PWRITE <= 1'b1;
+ M_APB_PPROT <= awskd_prot;
+ end
+
+ if (wskd_valid)
+ begin
+ M_APB_PWDATA <= wskd_data;
+ M_APB_PWSTRB <= wskd_strb;
+ end
+
+ M_APB_PENABLE <= 1'b0;
+ end else if (!M_APB_PENABLE)
+ M_APB_PENABLE <= 1'b1;
+ else if (M_APB_PREADY)
+ begin // if (M_APB_PSEL && M_APB_ENABLE)
+ M_APB_PENABLE <= 1'b0;
+ M_APB_PSEL <= 1'b0;
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ M_APB_PSEL <= 1'b0;
+ M_APB_PENABLE <= 1'b0;
+ end
+ end
+ // }}}
+
+ reg r_apb_bvalid, r_apb_rvalid, r_apb_error;
+ reg [DW-1:0] r_apb_data;
+
+ generate if (OPT_OUTGOING_SKIDBUFFER)
+ begin : GEN_OSKID
+ // {{{
+ // r_apb_bvalid, r_apb_rvalid, r_apb_error, r_apb_data
+ // {{{
+ initial r_apb_bvalid = 1'b0;
+ initial r_apb_rvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (M_APB_PSEL && M_APB_PENABLE && M_APB_PREADY)
+ begin
+ r_apb_bvalid <= (S_AXI_BVALID && !S_AXI_BREADY) && M_APB_PWRITE;
+ r_apb_rvalid <= (S_AXI_RVALID && !S_AXI_RREADY) && !M_APB_PWRITE;
+ if (!M_APB_PWRITE)
+ r_apb_data <= M_APB_PRDATA;
+ r_apb_error <= M_APB_PSLVERR;
+ end else begin
+ if (S_AXI_BREADY)
+ r_apb_bvalid <= 1'b0;
+ if (S_AXI_RREADY)
+ r_apb_rvalid <= 1'b0;
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ r_apb_bvalid <= 1'b0;
+ r_apb_rvalid <= 1'b0;
+ end
+ end
+ // }}}
+
+ // apb_bvalid
+ // {{{
+ always @(*)
+ apb_bvalid = (M_APB_PSEL && M_APB_PENABLE
+ && M_APB_PREADY && M_APB_PWRITE)|| r_apb_bvalid;
+ // }}}
+
+ // apb_rvalid
+ // {{{
+ always @(*)
+ apb_rvalid = (M_APB_PSEL && M_APB_PENABLE
+ && M_APB_PREADY && !M_APB_PWRITE)||r_apb_rvalid;
+ // }}}
+
+ // apb_data
+ // {{{
+ always @(*)
+ if (out_skid_full)
+ apb_data = r_apb_data;
+ else
+ apb_data = M_APB_PRDATA;
+ // }}}
+
+ // apb_error
+ // {{{
+ always @(*)
+ if (out_skid_full)
+ apb_error = r_apb_error;
+ else
+ apb_error = M_APB_PSLVERR;
+ // }}}
+
+ always @(*)
+ out_skid_full = r_apb_bvalid || r_apb_rvalid;
+ // }}}
+ end else begin : NO_OSKID
+ // {{{
+
+ initial r_apb_bvalid = 1'b0;
+ initial r_apb_rvalid = 1'b0;
+ initial r_apb_error = 1'b0;
+ initial r_apb_data = 0;
+ always @(*)
+ begin
+ r_apb_bvalid = 1'b0;
+ r_apb_rvalid = 1'b0;
+ r_apb_error = 1'b0;
+ r_apb_data = 0;
+
+ apb_bvalid = M_APB_PSEL && M_APB_PENABLE
+ && M_APB_PREADY && M_APB_PWRITE;
+
+ apb_rvalid = M_APB_PSEL && M_APB_PENABLE
+ && M_APB_PREADY && !M_APB_PWRITE;
+
+ apb_data = M_APB_PRDATA;
+
+ apb_error = M_APB_PSLVERR;
+
+ out_skid_full = 1'b0;
+ end
+
+ // Verilator lint_off UNUSED
+ wire skd_unused;
+ assign skd_unused = &{ 1'b0, r_apb_bvalid, r_apb_rvalid,
+ r_apb_data, r_apb_error, out_skid_full };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite write return signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // BVALID
+ // {{{
+ initial S_AXI_BVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_BVALID <= 1'b0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ S_AXI_BVALID <= apb_bvalid;
+ // }}}
+
+ // BRESP
+ // {{{
+ initial S_AXI_BRESP = 2'b00;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_BRESP <= 2'b00;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ S_AXI_BRESP <= { apb_error, 1'b0 };
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite read return signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ // RVALID
+ // {{{
+ initial S_AXI_RVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_RVALID <= 1'b0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ S_AXI_RVALID <= apb_rvalid;
+ // }}}
+
+ // RRESP
+ // {{{
+ initial S_AXI_RRESP = 2'b00;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_RRESP <= 2'b00;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ S_AXI_RRESP <= { apb_error, 1'b0 };
+ // }}}
+
+ // RDATA
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if ((!S_AXI_RVALID || S_AXI_RREADY) && apb_rvalid)
+ S_AXI_RDATA <= apb_data;
+ // }}}
+
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_AWADDR[AXILLSB-1:0], S_AXI_ARADDR[AXILLSB-1:0]
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = 3;
+
+ wire [F_LGDEPTH-1:0] faxi_rd_outstanding,
+ faxi_wr_outstanding,
+ faxi_awr_outstanding;
+ reg f_past_valid;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH (C_AXI_DATA_WIDTH),
+ .F_OPT_COVER_BURST(4),
+ .F_AXI_MAXWAIT(17),
+ .F_AXI_MAXDELAY(17),
+ .F_AXI_MAXRSTALL(3),
+ .F_LGDEPTH(F_LGDEPTH)
+ // }}}
+ ) faxil (
+ // {{{
+ .i_clk(S_AXI_ACLK), // System clock
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awprot(S_AXI_AWPROT),
+ //
+ .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_bresp(S_AXI_BRESP),
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arprot(S_AXI_ARPROT),
+ //
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rdata(S_AXI_RDATA),
+ //
+ .f_axi_rd_outstanding(faxi_rd_outstanding),
+ .f_axi_wr_outstanding(faxi_wr_outstanding),
+ .f_axi_awr_outstanding(faxi_awr_outstanding)
+ // }}}
+ );
+
+ // Correlate outstanding counters against our state
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assert(faxi_awr_outstanding == (S_AXI_AWREADY ? 0:1)
+ + (r_apb_bvalid ? 1:0)
+ + (S_AXI_BVALID ? 1:0)
+ + ((M_APB_PSEL && M_APB_PWRITE) ? 1:0));
+
+ assert(faxi_wr_outstanding == (S_AXI_WREADY ? 0:1)
+ + (r_apb_bvalid ? 1:0)
+ + (S_AXI_BVALID ? 1:0)
+ + ((M_APB_PSEL && M_APB_PWRITE) ? 1:0));
+
+ assert(faxi_rd_outstanding == (S_AXI_ARREADY ? 0:1)
+ + (r_apb_rvalid ? 1:0)
+ + (S_AXI_RVALID ? 1:0)
+ + ((M_APB_PSEL && !M_APB_PWRITE) ? 1:0));
+
+ if (r_apb_bvalid)
+ assert(S_AXI_BVALID);
+ if (r_apb_rvalid)
+ assert(S_AXI_RVALID);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // APB interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ fapb_master #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .F_OPT_MAXSTALL(3)
+ // }}}
+ ) fapb (
+ // {{{
+ .PCLK(S_AXI_ACLK), .PRESETn(S_AXI_ARESETN),
+ .PSEL( M_APB_PSEL),
+ .PENABLE(M_APB_PENABLE),
+ .PREADY( M_APB_PREADY),
+ .PADDR( M_APB_PADDR),
+ .PWRITE( M_APB_PWRITE),
+ .PWDATA( M_APB_PWDATA),
+ .PWSTRB( M_APB_PWSTRB),
+ .PPROT( M_APB_PPROT),
+ .PRDATA( M_APB_PRDATA),
+ .PSLVERR(M_APB_PSLVERR)
+ // }}}
+ );
+
+ always @(*)
+ if (!M_APB_PSEL)
+ assert(!M_APB_PENABLE);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction invariants
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ assert(!axil_write_ready || !axil_read_ready);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ (* anyconst *) reg f_never_test;
+ (* anyconst *) reg [AW-1:0] f_never_addr;
+ (* anyconst *) reg [DW-1:0] f_never_data;
+ (* anyconst *) reg [DW/8-1:0] f_never_strb;
+ (* anyconst *) reg [2:0] f_never_prot;
+
+ // Assume the never values are never received
+ // {{{
+ always @(*)
+ if (f_never_test)
+ begin
+ assume(f_never_addr[AXILLSB-1:0] == 0);
+
+ if (S_AXI_AWVALID)
+ begin
+ assume(S_AXI_AWADDR[AW-1:AXILLSB] != f_never_addr[AW-1:AXILLSB]);
+ assume(S_AXI_AWPROT != f_never_prot);
+ end
+
+ if (S_AXI_WVALID)
+ begin
+ assume(S_AXI_WDATA != f_never_data);
+ assume(S_AXI_WSTRB != f_never_strb);
+ end
+
+ if (S_AXI_ARVALID)
+ begin
+ assume(S_AXI_ARADDR[AW-1:AXILLSB] != f_never_addr[AW-1:AXILLSB]);
+ assume(S_AXI_ARPROT != f_never_prot);
+ end
+
+ if (M_APB_PSEL && M_APB_PENABLE && M_APB_PREADY&& !M_APB_PWRITE)
+ assume(M_APB_PRDATA != f_never_data);
+ end
+ // }}}
+
+ // Assert the never values are never in the incoming skid buffers
+ // {{{
+ always @(*)
+ if (f_never_test)
+ begin
+ if (awskd_valid)
+ begin
+ assert(awskd_addr != f_never_addr[AW-1:AXILLSB]);
+ assert(awskd_prot != f_never_prot);
+ end
+
+ if (wskd_valid)
+ begin
+ assert(wskd_data != f_never_data);
+ assert(wskd_strb != f_never_strb);
+ end
+
+ if (arskd_valid)
+ begin
+ assert(arskd_addr != f_never_addr[AW-1:AXILLSB]);
+ assert(arskd_prot != f_never_prot);
+ end
+
+ if (r_apb_rvalid)
+ assert(r_apb_data != f_never_data);
+ end
+ // }}}
+
+ // Assert the never values are never output
+ // {{{
+ always @(*)
+ if (f_never_test)
+ begin
+ if (M_APB_PSEL)
+ begin
+ assert(M_APB_PADDR != f_never_addr);
+ assert(M_APB_PPROT != f_never_prot);
+ if (M_APB_PWRITE)
+ begin
+ assert(M_APB_PWDATA != f_never_data);
+ assert(M_APB_PWSTRB != f_never_strb);
+ end
+ end
+
+ if (S_AXI_RVALID)
+ assert(S_AXI_RDATA != f_never_data);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // None (yet)
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axil2axis.v b/rtl/wb2axip/axil2axis.v
new file mode 100644
index 0000000..0e1d14f
--- /dev/null
+++ b/rtl/wb2axip/axil2axis.v
@@ -0,0 +1,883 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axil2axis
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Demonstrates a simple AXI-Lite interface to drive an AXI-Stream
+// channel. This can then be used to debug DSP processing.
+//
+// Registers: This AXI-lite to AXI-Stream core supports four word-sized
+// addresses. Byte enables are ignored.
+//
+// 2'b00, ADDR_SINK
+// Writes to this register will send data to the stream master,
+// with TLAST clear. Data goes first through a FIFO. If the
+// FIFO is full, the write will stall. If it stalls OPT_TIMEOUT
+// cycles, the write will fail and return a bus error.
+//
+// Reads from this register will return data from the stream slave,
+// but without consuming it. Values read here may still be read
+// from the ADDR_SOURCE register later.
+//
+// 2'b01, ADDR_SOURCE
+// Writes to this register will send data downstream to the stream
+// master as well, but this time with TLAST set.
+//
+// Reads from this register will accept a value from the stream
+// slave interface. The read value contains TDATA. TLAST is
+// ignored in this read. If you want access to TLAST, you can get
+// it from the ADDR_FIFO register.
+//
+// If there is no data to be read, the read will not and does not
+// stall. It will instead return a bus error.
+//
+// 2'b10, ADDR_STATS
+// Since we can, we'll handle some statistics here. The top half
+// word contains two counters: a 4-bit counter of TLAST's issued
+// from the stream master, and a 12-bit counter of TDATA values
+// issued. Neither counter includes data still contained in the
+// FIFO. If the OPT_SOURCE option is clear, these values will
+// always be zero.
+//
+// The second (bottom, or least-significant) halfword contains the
+// same regarding the stream slave. If OPT_SINK is set, these
+// counters count values read from the core. If OPT_SINK is clear,
+// so that the stream sink is not truly implemented, then TREADY
+// will be held high and the counter will just count values coming
+// into the core never going into the FIFO.
+//
+// 2'b11, ADDR_FIFO
+// Working with the core can be a challenge. You want to make
+// certain that writing to the core doesn't hang the design, and
+// that reading from the core doesn't cause a bus error.
+//
+// Bits 31:16 contain the number of items in the write FIFO, and
+// bits 14:0 contain the number of items in the read FIFO.
+//
+// Bit 15 contains whether or not the next item to be read is
+// the last item in a packet, i.e. with TLAST set.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axil2axis #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ parameter C_AXI_ADDR_WIDTH = 4,
+ localparam C_AXI_DATA_WIDTH = 32,
+ parameter C_AXIS_DATA_WIDTH = 16,
+ //
+ // OPT_SOURCE enables the AXI stream master logic. If not
+ // enabled, M_AXI_TVALID will be held at zero, and the stream
+ // master logic may be ignored.
+ parameter [0:0] OPT_SOURCE = 1'b1,
+ //
+ // OPT_SINK enables the AXI stream slave logic. If not enabled,
+ // reads will always return zero, and S_AXIS_TREADY will be
+ // held high.
+ parameter [0:0] OPT_SINK = 1'b1,
+ //
+ // If OPT_SIGN_EXTEND is set, values received will be sign
+ // extended to fill the full data width on read. Otherwise
+ // the most significant of any unused bits will remain clear.
+ parameter [0:0] OPT_SIGN_EXTEND = 1'b0,
+ //
+ // Data written to this core will be placed into a FIFO before
+ // entering the AXI stream master. LGFIFO is the log, based
+ // two, of the number of words in this FIFO. Similarly, data
+ // consumed by AXI stream slave contained in this core will go
+ // first into a read FIFO. Reads from the core will then return
+ // data from this FIFO, or a bus error if none is available.
+ parameter LGFIFO = 5,
+ //
+ // OPT_TIMEOUT, if non-zero, will allow writes to the stream
+ // master, or reads from the stream slave, to stall the core
+ // for OPT_TIMEOUT cycles for the stream to be ready. If the
+ // stream isn't ready at this time (i.e. if the write FIFO is
+ // still full, or the read FIFO still empty), the result will
+ // be returned as a bus error. Likewise, if OPT_TIMEOUT==0,
+ // the core will always return a bus error if ever the write
+ // FIFO is full or the read FIFO empty.
+ parameter OPT_TIMEOUT = 5,
+ //
+ // OPT_LOWPOWER sets outputs to zero if not valid. This applies
+ // to the AXI-lite bus, however, and not the AXI stream FIFOs,
+ // since those don't have LOWPOWER support (currently).
+ parameter [0:0] OPT_LOWPOWER = 0
+ //
+ // This design currently ignores WSTRB, beyond checking that it
+ // is not zero. I see no easy way to add it. (I'll leave that
+ // to you to implement, if you wish.)
+ // parameter [0:0] OPT_WSTRB = 0,
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI-lite signals
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ // }}}
+ // AXI stream slave (sink) signals
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire S_AXIS_TLAST,
+ // }}}
+ // AXI stream master (source) signals
+ // {{{
+ output wire M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output reg M_AXIS_TLAST
+ // }}}
+ // }}}
+ );
+
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [1:0] ADDR_SINK = 2'b00, // Read from stream
+ ADDR_SOURCE = 2'b01, // Write, also sets TLAST
+ ADDR_STATS = 2'b10,
+ ADDR_FIFO = 2'b11;
+ localparam SW = C_AXIS_DATA_WIDTH;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr;
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid, axil_berr;
+ //
+ wire axil_read_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ wire awskd_valid, wskd_valid;
+ wire wfifo_full, wfifo_write, wfifo_empty;
+ wire [LGFIFO:0] wfifo_fill;
+ reg write_timeout;
+
+ wire read_timeout;
+ reg axil_rerr;
+ reg [3:0] read_bursts_completed;
+ reg [11:0] reads_completed;
+
+ wire [3:0] write_bursts_completed;
+ wire [11:0] writes_completed;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY)
+ && ((awskd_addr[1] != ADDR_SOURCE[1])
+ || (!wfifo_full || write_timeout));
+
+ //
+ // Write timeout generation
+ //
+ // {{{
+ generate if ((OPT_TIMEOUT > 1) && OPT_SOURCE)
+ begin : GEN_WRITE_TIMEOUT
+ reg r_write_timeout;
+ reg [$clog2(OPT_TIMEOUT)-1:0] write_timer;
+
+ initial write_timer = OPT_TIMEOUT-1;
+ initial r_write_timeout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ write_timer <= OPT_TIMEOUT-1;
+ r_write_timeout<= 1'b0;
+ end else if (!awskd_valid || !wfifo_full || !wskd_valid
+ || (awskd_addr[1] != ADDR_SOURCE[1])
+ || (S_AXI_BVALID && !S_AXI_BREADY))
+ begin
+ write_timer <= OPT_TIMEOUT-1;
+ r_write_timeout<= 1'b0;
+ end else begin
+ if (write_timer > 0)
+ write_timer <= write_timer - 1;
+ r_write_timeout <= (write_timer <= 1);
+ end
+
+ assign write_timeout = r_write_timeout;
+`ifdef FORMAL
+ always @(*)
+ assert(write_timer <= OPT_TIMEOUT-1);
+ always @(*)
+ assert(write_timeout == (write_timer == 0));
+`endif
+ end else begin : NO_WRITE_TIMEOUT
+
+ assign write_timeout = 1'b1;
+
+ end endgenerate
+ // }}}
+
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+
+ initial axil_berr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && i_reset)
+ axil_berr <= 0;
+ else if (axil_write_ready)
+ axil_berr <= (wfifo_full)&&(awskd_addr[1]==ADDR_SOURCE[1]);
+ else if (OPT_LOWPOWER && S_AXI_BREADY)
+ axil_berr <= 1'b0;
+
+ assign S_AXI_BRESP = { axil_berr, 1'b0 };
+ // }}}
+
+ //
+ // AXI-stream source (Write) FIFO
+ //
+ // {{{
+ assign wfifo_write = axil_write_ready && awskd_addr[1]==ADDR_SOURCE[1]
+ && wskd_strb != 0 && !wfifo_full;
+
+ generate if (OPT_SOURCE)
+ begin : GEN_SOURCE_FIFO
+
+ sfifo #(.BW(SW+1), .LGFLEN(LGFIFO))
+ source(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(wfifo_write),
+ .i_data({awskd_addr[0]==ADDR_SOURCE[0],
+ wskd_data[SW-1:0]}),
+ .o_full(wfifo_full), .o_fill(wfifo_fill),
+ .i_rd(M_AXIS_TREADY),
+ .o_data({ M_AXIS_TLAST, M_AXIS_TDATA }),
+ .o_empty(wfifo_empty));
+
+ assign M_AXIS_TVALID = !wfifo_empty;
+
+ end else begin : NO_SOURCE_FIFO
+
+ assign M_AXIS_TVALID = 1'b0;
+ assign M_AXIS_TDATA = 0;
+ assign M_AXIS_TLAST = 0;
+
+ assign wfifo_full = 1'b0;
+ assign wfifo_fill = 0;
+
+ end endgenerate
+ // }}}
+
+ //
+ // AXI-stream consumer/sink (Read) FIFO
+ //
+ // {{{
+ wire rfifo_empty, rfifo_full, rfifo_last, read_rfifo;
+ wire [LGFIFO:0] rfifo_fill;
+ wire [SW-1:0] rfifo_data;
+
+ generate if (OPT_SINK)
+ begin : GEN_SINK_FIFO
+
+ sfifo #(.BW(SW+1), .LGFLEN(LGFIFO))
+ sink(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(S_AXIS_TVALID && S_AXIS_TREADY),
+ .i_data({S_AXIS_TLAST, S_AXIS_TDATA}),
+ .o_full(rfifo_full), .o_fill(rfifo_fill),
+ .i_rd(read_rfifo),
+ .o_data({ rfifo_last, rfifo_data }),
+ .o_empty(rfifo_empty));
+
+ assign S_AXIS_TREADY = !rfifo_full;
+ assign read_rfifo =(axil_read_ready && arskd_addr== ADDR_SINK)
+ && !rfifo_empty;
+
+ end else begin : NO_SINK
+
+ assign S_AXIS_TREADY = 1'b1;
+
+ assign rfifo_empty = 1'b1;
+ assign rfifo_data = 0;
+ assign rfifo_last = 1'b1;
+ assign rfifo_fill = 0;
+
+ end endgenerate
+ // }}}
+
+ //
+ // Read timeout generation
+ //
+ // {{{
+ generate if (OPT_SINK && OPT_TIMEOUT > 1)
+ begin : GEN_READ_TIMEOUT
+ reg [$clog2(OPT_TIMEOUT)-1:0] read_timer;
+ reg r_read_timeout;
+
+ initial read_timer = OPT_TIMEOUT-1;
+ initial r_read_timeout = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ read_timer <= OPT_TIMEOUT-1;
+ r_read_timeout<= 1'b0;
+ end else if (!arskd_valid || (S_AXI_RVALID && !S_AXI_RREADY)
+ ||!rfifo_empty
+ ||(arskd_addr[1] != ADDR_SINK[1]))
+ begin
+ read_timer <= OPT_TIMEOUT-1;
+ r_read_timeout<= 1'b0;
+ end else begin
+ if (read_timer > 0)
+ read_timer <= read_timer - 1;
+ r_read_timeout <= (read_timer <= 1);
+ end
+
+ assign read_timeout = r_read_timeout;
+
+`ifdef FORMAL
+ always @(*)
+ assert(read_timer <= OPT_TIMEOUT-1);
+ always @(*)
+ assert(read_timeout == (read_timer == 0));
+`endif
+ end else begin : NO_READ_TIMEOUT
+
+ assign read_timeout = 1'b1;
+
+ end endgenerate
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+ wire arskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!S_AXI_RVALID || S_AXI_RREADY)
+ && ((arskd_addr[1] != ADDR_SINK[1])
+ || (!rfifo_empty || read_timeout));
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_rerr <= 1'b0;
+ else if (axil_read_ready)
+ axil_rerr <= rfifo_empty && (arskd_addr[1] == ADDR_SINK[1]);
+ else if (OPT_LOWPOWER && S_AXI_RREADY)
+ axil_rerr <= 1'b0;
+
+ assign S_AXI_RRESP = { axil_rerr, 1'b0 };
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Read data counting : reads_completed and read_bursts_completed
+ // {{{
+ initial reads_completed = 0;
+ initial read_bursts_completed = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ // {{{
+ reads_completed <= 0;
+ read_bursts_completed <= 0;
+ // }}}
+ end else if (!OPT_SINK)
+ begin
+ // {{{
+ reads_completed <= reads_completed + (S_AXIS_TVALID ? 1:0);
+ read_bursts_completed <= read_bursts_completed
+ + ((S_AXIS_TVALID && S_AXIS_TLAST) ? 1:0);
+ // }}}
+ end else if (read_rfifo && !rfifo_empty)
+ begin
+ // {{{
+ reads_completed <= reads_completed + 1;
+ read_bursts_completed <= read_bursts_completed + (rfifo_last ? 1:0);
+ // }}}
+ end
+ // }}}
+
+ //
+ // Write data counting
+ // {{{
+ generate if (OPT_SOURCE)
+ begin : GEN_WRITES_COMPLETED
+ // {{{
+ reg [3:0] r_write_bursts_completed;
+ reg [11:0] r_writes_completed;
+
+ initial r_writes_completed = 0;
+ initial r_write_bursts_completed = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_writes_completed <= 0;
+ r_write_bursts_completed <= 0;
+ end else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ begin
+ r_writes_completed <= writes_completed + 1;
+ r_write_bursts_completed <= write_bursts_completed
+ + (M_AXIS_TLAST ? 1:0);
+ end
+
+ assign writes_completed = r_writes_completed;
+ assign write_bursts_completed = r_write_bursts_completed;
+ // }}}
+ end else begin : NO_COMPLETION_COUNTERS // No AXI-stream source logic
+ // {{{
+ assign writes_completed = 0;
+ assign write_bursts_completed = 0;
+ // }}}
+ end endgenerate
+ // }}}
+
+ //
+ // Read data register
+ // {{{
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ axil_read_data <= 0;
+ casez(arskd_addr)
+ { ADDR_SINK[1], 1'b? }: begin
+ if (OPT_SIGN_EXTEND && rfifo_data[SW-1])
+ axil_read_data <= -1;
+ axil_read_data[SW-1:0] <= rfifo_data;
+ end
+ ADDR_STATS: begin
+ axil_read_data[31:28] <= write_bursts_completed;
+ axil_read_data[27:16] <= writes_completed;
+ axil_read_data[15:12] <= read_bursts_completed;
+ axil_read_data[11:0] <= reads_completed;
+ end
+ ADDR_FIFO: begin
+ // FIFO information
+ axil_read_data[16 +: LGFIFO+1] <= wfifo_fill;
+ axil_read_data[15] <= rfifo_last;
+ axil_read_data[LGFIFO:0] <= rfifo_fill;
+ end
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+
+ assign S_AXI_RDATA = axil_read_data;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_ARADDR[ADDRLSB-1:0],
+ S_AXI_AWADDR[ADDRLSB-1:0],
+ wskd_data[C_AXI_DATA_WIDTH-1:SW] };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used in verfiying this core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Register definitions
+ // {{{
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(OPT_TIMEOUT + 2),
+ .F_AXI_MAXDELAY(OPT_TIMEOUT + 2),
+ .F_AXI_MAXRSTALL(2),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .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)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Verifying the packet counters
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [11:0] f_reads, f_writes;
+ reg [3:0] f_read_pkts, f_write_pkts;
+
+ //
+ // Mirror the read counter
+ //
+ initial f_reads = 0;
+ initial f_read_pkts = 0;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ f_reads <= 0;
+ f_read_pkts <= 0;
+ end else if (OPT_SINK && (axil_read_ready
+ && arskd_addr == ADDR_SINK && !rfifo_empty))
+ begin
+ f_reads <= f_reads + 1;
+ f_read_pkts <= f_read_pkts + (rfifo_last ? 1:0);
+ end else if (!OPT_SINK && S_AXIS_TVALID)
+ begin
+ f_reads <= f_reads + 1;
+ f_read_pkts <= f_read_pkts + (S_AXIS_TLAST ? 1:0);
+ end
+
+ always @(*)
+ assert(f_reads == reads_completed);
+ always @(*)
+ assert(f_read_pkts == read_bursts_completed);
+
+ always @(*)
+ if (!OPT_SINK)
+ assert(S_AXIS_TREADY);
+
+ //
+ // Mirror the write counter
+ //
+ initial f_writes = 0;
+ initial f_write_pkts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ f_writes <= 0;
+ f_write_pkts <= 0;
+ end else if (OPT_SOURCE && M_AXIS_TVALID && M_AXIS_TREADY)
+ begin
+ f_writes <= f_writes + 1;
+ f_write_pkts <= f_write_pkts + (M_AXIS_TLAST ? 1:0);
+ end
+
+ always @(*)
+ if (!OPT_SOURCE)
+ begin
+ assert(f_writes == 0);
+ assert(f_write_pkts == 0);
+ end
+
+ always @(*)
+ begin
+ assert(f_writes == writes_completed);
+ assert(f_write_pkts == write_bursts_completed);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Verify the read result
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN && axil_read_ready))
+ begin
+ assert(S_AXI_RVALID);
+ case($past(arskd_addr))
+ ADDR_SINK: begin
+ assert(S_AXI_RDATA[SW-1:0] == $past(rfifo_data));
+ if (SW < C_AXI_DATA_WIDTH)
+ begin
+ if (OPT_SIGN_EXTEND && $past(rfifo_data[SW-1]))
+ assert(&S_AXI_RDATA[C_AXI_DATA_WIDTH-1:SW]);
+ else
+ assert(S_AXI_RDATA[C_AXI_DATA_WIDTH-1:SW] == 0);
+ end end
+ // 1: assert(S_AXI_RDATA == $past(r1));
+ ADDR_STATS: begin
+ assert(S_AXI_RRESP == 2'b00);
+ assert(S_AXI_RDATA[31:28]
+ == $past(write_bursts_completed));
+ assert(S_AXI_RDATA[27:16] == $past(writes_completed));
+
+ assert(S_AXI_RDATA[15:12]
+ == $past(read_bursts_completed));
+ assert(S_AXI_RDATA[11:0] == $past(reads_completed));
+ end
+ ADDR_FIFO: begin
+ assert(S_AXI_RRESP == 2'b00);
+ if (LGFIFO < 16)
+ assert(S_AXI_RDATA[31:16+LGFIFO+1] == 0);
+ assert(S_AXI_RDATA[16+: LGFIFO+1]==$past(wfifo_fill));
+ assert(S_AXI_RDATA[15] == $past(rfifo_last));
+ if (LGFIFO < 15)
+ assert(S_AXI_RDATA[14:LGFIFO+1] == 0);
+ assert(S_AXI_RDATA[ 0+: LGFIFO+1]==$past(rfifo_fill));
+ end
+ default: begin end
+ endcase
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXI_RVALID)
+ assert(S_AXI_RDATA == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-stream interfaces
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Slave/consumer properties
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || !$past(S_AXI_ARESETN))
+ begin
+ assume(!S_AXIS_TVALID);
+ end else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ begin
+ assume(S_AXIS_TVALID);
+ assume($stable(S_AXIS_TDATA));
+ assume($stable(S_AXIS_TLAST));
+ end
+
+ // Master/producer/source properties
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || !$past(S_AXI_ARESETN))
+ begin
+ assert(!M_AXIS_TVALID);
+ end else if ($past(M_AXIS_TVALID && !M_AXIS_TREADY))
+ begin
+ assert(M_AXIS_TVALID);
+ assert($stable(M_AXIS_TDATA));
+ assert($stable(M_AXIS_TLAST));
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ cover(S_AXI_ARESETN && writes_completed == 16);
+
+ always @(*)
+ cover(S_AXI_ARESETN && reads_completed == 16);
+
+ always @(*)
+ cover(S_AXI_ARESETN && writes_completed == 16
+ && reads_completed == 16);
+
+ always @(*)
+ cover(S_AXI_BVALID && S_AXI_BRESP != 2'b00);
+
+ always @(*)
+ cover(S_AXI_RVALID && S_AXI_RRESP != 2'b00);
+
+ // }}}
+ // }}}
+`endif
+endmodule
diff --git a/rtl/wb2axip/axildouble.v b/rtl/wb2axip/axildouble.v
new file mode 100644
index 0000000..ddaedb6
--- /dev/null
+++ b/rtl/wb2axip/axildouble.v
@@ -0,0 +1,734 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axildouble.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Create a special slave which can be used to reduce crossbar
+// logic for multiple simplified slaves. This is a companion
+// core to the similar axilsingle core, but allowing the slave to
+// decode the clock between multiple possible addresses.
+//
+// To use this, the slave must follow specific (simplified AXI) rules:
+//
+// Write interface
+// 1. The slave must guarantee that AWREADY == WREADY = 1
+// (This core doesn't have AWREADY or WREADY inputs)
+// 2. The slave must also guarantee that BVALID == $past(AWVALID)
+// (This core internally generates BVALID)
+// 3. The controller will guarantee that AWVALID == WVALID
+// (You can connect AWVALID to WVALID when connecting to your core)
+// 4. The controller will also guarantee that BREADY = 1
+// (This core doesn't have a BVALID input)
+//
+// Read interface
+// 1. The slave must guarantee that ARREADY == 1
+// (This core doesn't have an ARREADY input)
+// 2. The slave must also guarantee that RVALID == $past(ARVALID)
+// (This core doesn't have an RVALID input, trusting the slave
+// instead)
+// 3. The controller will guarantee that RREADY = 1
+// (This core doesn't have an RREADY output)
+//
+//
+// Why? This simplifies slave logic. Slaves may interact with the bus
+// using only the logic below:
+//
+// always @(posedge S_AXI_ACLK)
+// if (AWVALID) case(AWADDR)
+// R1: slvreg_1 <= WDATA;
+// R2: slvreg_2 <= WDATA;
+// R3: slvreg_3 <= WDATA;
+// R4: slvreg_4 <= WDATA;
+// endcase
+//
+// always @(*)
+// BRESP = 2'b00;
+//
+// always @(posedge S_AXI_ACLK)
+// if (ARVALID)
+// case(ARADDR)
+// R1: RDATA <= slvreg_1;
+// R2: RDATA <= slvreg_2;
+// R3: RDATA <= slvreg_3;
+// R4: RDATA <= slvreg_4;
+// endcase
+//
+// always @(*)
+// RRESP = 2'b00;
+//
+// This core will then keep track of the more complex bus logic,
+// simplifying both slaves and connection logic. Slaves with the more
+// complicated (and proper/accurate) logic, that follow the rules above,
+// should have no problems with this additional logic.
+//
+// Performance:
+//
+// This core can sustain one read/write per clock as long as the upstream
+// AXI master keeps S_AXI_[BR]READY high. If S_AXI_[BR]READY ever drops,
+// there's some flexibility provided by the return FIFO, so the master
+// might not notice a drop in throughput until the FIFO fills.
+//
+// The more practical performance measure is the latency of this core.
+// That I've measured at four clocks.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// `ifdef VERILATOR
+// `define FORMAL
+// `endif
+// }}}
+module axildouble #(
+ // {{{
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 32,
+ //
+ // NS is the number of slave interfaces
+ parameter NS = 8,
+ //
+ //
+ parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_ADDR = {
+ { 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 3'b110, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 3'b101, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 3'b100, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 3'b011, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 3'b010, {(C_AXI_ADDR_WIDTH-3){1'b0}} },
+ { 4'b0001,{(C_AXI_ADDR_WIDTH-4){1'b0}} },
+ { 4'b0000,{(C_AXI_ADDR_WIDTH-4){1'b0}} } },
+ //
+ //
+ parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_MASK =
+ (NS <= 1) ? 0
+ : { {(NS-2){ 3'b111,{(C_AXI_ADDR_WIDTH-3){1'b0}} }},
+ {(2){ 4'b1111,{(C_AXI_ADDR_WIDTH-4){1'b0}} }}
+ },
+ //
+ //
+ // LGFLEN specifies the log (based two) of the number of
+ // transactions that may need to be held outstanding internally.
+ // If you really want high throughput, and if you expect any
+ // back pressure at all, then increase LGFLEN. Otherwise the
+ // default value of 3 (FIFO size = 8) should be sufficient
+ // to maintain full loading
+ parameter LGFLEN=3,
+ //
+ // If set, OPT_LOWPOWER will set all unused registers, both
+ // internal and external, to zero anytime their corresponding
+ // *VALID bit is clear
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [3-1:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire [2-1:0] S_AXI_BRESP,
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ //
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [3-1:0] S_AXI_ARPROT,
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ //
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [2-1:0] S_AXI_RRESP,
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ //
+ //
+ //
+ output wire [NS-1:0] M_AXI_AWVALID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [3-1:0] M_AXI_AWPROT,
+ //
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ //
+ input wire [NS*2-1:0] M_AXI_BRESP,
+ //
+ output wire [NS-1:0] M_AXI_ARVALID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [3-1:0] M_AXI_ARPROT,
+ //
+ input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [NS*2-1:0] M_AXI_RRESP
+ // }}}
+ );
+
+ //
+ // AW, and DW, are short-hand abbreviations used locally.
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam LGNS = $clog2(NS);
+ //
+ localparam INTERCONNECT_ERROR = 2'b11;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic:
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire awskid_valid, bffull, bempty, write_awskidready,
+ dcd_awvalid;
+ reg write_bvalid, write_response;
+ reg bfull, write_wready, write_no_index;
+ wire [NS:0] wdecode;
+ wire [AW-1:0] awskid_addr;
+ wire [AW-1:0] m_awaddr;
+ reg [LGNS-1:0] write_windex, write_bindex;
+ wire [3-1:0] awskid_prot, m_axi_awprot;
+ wire [LGFLEN:0] bfill;
+ reg [LGFLEN:0] write_count;
+ reg [1:0] write_resp;
+ integer k;
+
+ skidbuffer #(.OPT_LOWPOWER(OPT_LOWPOWER), .OPT_OUTREG(0),
+ .DW(AW+3))
+ awskid( .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID),
+ .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWPROT, S_AXI_AWADDR }),
+ .o_valid(awskid_valid), .i_ready(write_awskidready),
+ .o_data({ awskid_prot, awskid_addr }));
+
+ wire awskd_stall;
+
+ addrdecode #(.AW(AW), .DW(3), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(1'b1))
+ wraddr(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(awskid_valid && write_awskidready), .o_stall(awskd_stall),
+ .i_addr(awskid_addr),
+ .i_data(awskid_prot),
+ .o_valid(dcd_awvalid), .i_stall(!S_AXI_WVALID),
+ .o_decode(wdecode), .o_addr(m_awaddr),
+ .o_data(m_axi_awprot));
+
+ always @(*)
+ write_wready = dcd_awvalid;
+ assign S_AXI_WREADY = write_wready;
+ assign M_AXI_AWVALID = (S_AXI_WVALID) ? wdecode[NS-1:0] : 0;
+ assign M_AXI_AWADDR = m_awaddr;
+ assign M_AXI_AWPROT = m_axi_awprot;
+ assign M_AXI_WDATA = S_AXI_WDATA;
+ assign M_AXI_WSTRB = S_AXI_WSTRB;
+ assign write_awskidready = (S_AXI_WVALID || !S_AXI_WREADY) && !bfull;
+
+ always @(*)
+ begin
+ write_windex = 0;
+ for(k=0; k<NS; k=k+1)
+ if (wdecode[k])
+ write_windex = write_windex | k[LGNS-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ begin
+ write_bindex <= write_windex;
+ write_no_index <= wdecode[NS];
+ end
+
+ initial { write_response, write_bvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { write_response, write_bvalid } <= 0;
+ else
+ { write_response, write_bvalid }
+ <= { write_bvalid, (S_AXI_WVALID && S_AXI_WREADY) };
+
+ always @(posedge S_AXI_ACLK)
+ if (write_no_index)
+ write_resp <= INTERCONNECT_ERROR;
+ else
+ write_resp <= M_AXI_BRESP[2*write_bindex +: 2];
+
+ initial write_count = 0;
+ initial bfull = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ write_count <= 0;
+ bfull <= 0;
+ end else case({ (awskid_valid && write_awskidready),
+ (S_AXI_BVALID & S_AXI_BREADY) })
+ 2'b01: begin
+ write_count <= write_count - 1;
+ bfull <= 1'b0;
+ end
+ 2'b10: begin
+ write_count <= write_count + 1;
+ bfull <= (&write_count[LGFLEN-1:0]);
+ end
+ default: begin end
+ endcase
+
+`ifdef FORMAL
+ always @(*)
+ assert(write_count <= { 1'b1, {(LGFLEN){1'b0}} });
+ always @(*)
+ assert(bfull == (write_count == { 1'b1, {(LGFLEN){1'b0}} }));
+`endif
+
+ sfifo #(.BW(2), .OPT_ASYNC_READ(0), .LGFLEN(LGFLEN))
+ bfifo ( .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(write_response), .i_data(write_resp), .o_full(bffull),
+ .o_fill(bfill),
+ .i_rd(S_AXI_BVALID && S_AXI_BREADY), .o_data(S_AXI_BRESP),
+ .o_empty(bempty));
+
+`ifdef FORMAL
+ always @(*)
+ assert(write_count == bfill
+ + (write_response ? 1:0)
+ + (write_bvalid ? 1:0)
+ + (write_wready ? 1:0));
+
+`ifdef VERIFIC
+ always @(*)
+ if (bfifo.f_first_in_fifo)
+ assert(bfifo.f_first_data != 2'b01);
+ always @(*)
+ if (bfifo.f_second_in_fifo)
+ assert(bfifo.f_second_data != 2'b01);
+ always @(*)
+ if (!bempty && (!bfifo.f_first_in_fifo
+ || bfifo.rd_addr != bfifo.f_first_addr)
+ &&(!bfifo.f_second_in_fifo
+ || bfifo.rd_addr != bfifo.f_second_addr))
+ assume(S_AXI_BRESP != 2'b01);
+`else
+ always @(*)
+ if (!bempty)
+ assume(S_AXI_BRESP != 2'b01);
+`endif
+`endif
+
+ assign S_AXI_BVALID = !bempty;
+
+`ifdef FORMAL
+ always @(*)
+ assert(!bffull || !write_bvalid);
+`endif
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read logic
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire rempty, rdfull;
+ wire [LGFLEN:0] rfill;
+ reg [LGNS-1:0] read_index, last_read_index;
+ reg [1:0] read_resp;
+ reg [DW-1:0] read_rdata;
+ wire read_rwait, arskd_stall;
+ reg read_rvalid, read_result, read_no_index;
+ wire [AW-1:0] m_araddr;
+ wire [3-1:0] m_axi_arprot;
+ wire [NS:0] rdecode;
+
+ addrdecode #(.AW(AW), .DW(3), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(1'b1))
+ rdaddr(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID && S_AXI_ARREADY), .o_stall(arskd_stall),
+ .i_addr(S_AXI_ARADDR), .i_data(S_AXI_ARPROT),
+ .o_valid(read_rwait), .i_stall(1'b0),
+ .o_decode(rdecode), .o_addr(m_araddr),
+ .o_data(m_axi_arprot));
+
+ assign M_AXI_ARVALID = rdecode[NS-1:0];
+ assign M_AXI_ARADDR = m_araddr;
+ assign M_AXI_ARPROT = m_axi_arprot;
+
+ initial { read_result, read_rvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_result, read_rvalid } <= 2'b00;
+ else
+ { read_result, read_rvalid } <= { read_rvalid, read_rwait };
+
+ always @(*)
+ begin
+ read_index = 0;
+
+ for(k=0; k<NS; k=k+1)
+ if (rdecode[k])
+ read_index = read_index | k[LGNS-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ last_read_index <= read_index;
+
+ always @(posedge S_AXI_ACLK)
+ read_no_index <= rdecode[NS];
+
+ always @(posedge S_AXI_ACLK)
+ read_rdata <= M_AXI_RDATA[DW*last_read_index +: DW];
+
+ always @(posedge S_AXI_ACLK)
+ if (read_no_index)
+ read_resp <= INTERCONNECT_ERROR;
+ else
+ read_resp <= M_AXI_RRESP[2*last_read_index +: 2];
+
+ reg read_full;
+ reg [LGFLEN:0] read_count;
+
+ initial { read_count, read_full } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_count, read_full } <= 0;
+ else case({ S_AXI_ARVALID & S_AXI_ARREADY, S_AXI_RVALID & S_AXI_RREADY})
+ 2'b10: begin
+ read_count <= read_count + 1;
+ read_full <= &read_count[LGFLEN-1:0];
+ end
+ 2'b01: begin
+ read_count <= read_count - 1;
+ read_full <= 1'b0;
+ end
+ default: begin end
+ endcase
+
+`ifdef FORMAL
+ always @(*)
+ assert(read_count <= { 1'b1, {(LGFLEN){1'b0}} });
+ always @(*)
+ assert(read_full == (read_count == { 1'b1, {(LGFLEN){1'b0}} }));
+`endif
+ assign S_AXI_ARREADY = !read_full;
+
+ sfifo #(.BW(DW+2), .OPT_ASYNC_READ(0), .LGFLEN(LGFLEN))
+ rfifo ( .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(read_result), .i_data({ read_rdata, read_resp }),
+ .o_full(rdfull), .o_fill(rfill),
+ .i_rd(S_AXI_RVALID && S_AXI_RREADY),
+ .o_data({ S_AXI_RDATA, S_AXI_RRESP }),.o_empty(rempty));
+
+`ifdef FORMAL
+ always @(*)
+ assert(read_count == rfill + read_result + read_rvalid + read_rwait);
+`ifdef VERIFIC
+ always @(*)
+ if (rfifo.f_first_in_fifo)
+ assert(rfifo.f_first_data[1:0] != 2'b01);
+ always @(*)
+ if (rfifo.f_second_in_fifo)
+ assert(rfifo.f_second_data[1:0] != 2'b01);
+ always @(*)
+ if (!rempty && (!rfifo.f_first_in_fifo
+ || rfifo.rd_addr != rfifo.f_first_addr)
+ &&(!rfifo.f_second_in_fifo
+ || rfifo.rd_addr != rfifo.f_second_addr))
+ assume(S_AXI_RRESP != 2'b01);
+`else
+ always @(*)
+ if (!rempty)
+ assume(S_AXI_RRESP != 2'b01);
+`endif
+`endif
+
+ assign S_AXI_RVALID = !rempty;
+
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+ bffull, rdfull, bfill, rfill,
+ awskd_stall, arskd_stall };
+ // verilator lint_on UNUSED
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGFLEN+1;
+ reg f_past_valid;
+ reg [F_LGDEPTH-1:0] count_awr_outstanding, count_wr_outstanding,
+ count_rd_outstanding;
+
+
+ wire [(F_LGDEPTH-1):0] f_axi_awr_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_rd_outstanding;
+
+ wire [1:0] fm_axi_awr_outstanding [0:NS-1];
+ wire [1:0] fm_axi_wr_outstanding [0:NS-1];
+ wire [1:0] fm_axi_rd_outstanding [0:NS-1];
+
+ reg [NS-1:0] m_axi_rvalid, m_axi_bvalid;
+
+ faxil_slave #(// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ // .F_OPT_NO_READS(1'b0),
+ // .F_OPT_NO_WRITES(1'b0),
+ .F_OPT_XILINX(1),
+ .F_LGDEPTH(F_LGDEPTH))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awprot(S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp(S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arprot(S_AXI_ARPROT),
+ //
+ .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(f_axi_rd_outstanding),
+ .f_axi_wr_outstanding(f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding));
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ genvar M;
+ generate for(M=0; M<NS; M=M+1)
+ begin : CONSTRAIN_SLAVE_INTERACTIONS
+
+ faxil_master #(// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ // .F_OPT_NO_READS(1'b0),
+ // .F_OPT_NO_WRITES(1'b0),
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(2))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID[M]),
+ .i_axi_awready(1'b1),
+ .i_axi_awaddr(M_AXI_AWADDR),
+ .i_axi_awprot(M_AXI_AWPROT),
+ //
+ .i_axi_wvalid(M_AXI_AWVALID[M]),
+ .i_axi_wready(1'b1),
+ .i_axi_wdata(M_AXI_WDATA[C_AXI_DATA_WIDTH-1:0]),
+ .i_axi_wstrb(M_AXI_WSTRB[C_AXI_DATA_WIDTH/8-1:0]),
+ //
+ .i_axi_bvalid(m_axi_bvalid[M]),
+ .i_axi_bready(1'b1),
+ .i_axi_bresp(M_AXI_BRESP[2*M +: 2]),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID[M]),
+ .i_axi_arready(1'b1),
+ .i_axi_araddr(M_AXI_ARADDR),
+ .i_axi_arprot(M_AXI_ARPROT),
+ //
+ .i_axi_rdata(M_AXI_RDATA[M*C_AXI_DATA_WIDTH +: C_AXI_DATA_WIDTH]),
+ .i_axi_rresp(M_AXI_RRESP[2*M +: 2]),
+ .i_axi_rvalid(m_axi_rvalid[M]),
+ .i_axi_rready(1'b1),
+ //
+ .f_axi_rd_outstanding(fm_axi_rd_outstanding[M]),
+ .f_axi_wr_outstanding(fm_axi_wr_outstanding[M]),
+ .f_axi_awr_outstanding(fm_axi_awr_outstanding[M]));
+
+ initial m_axi_bvalid <= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_bvalid[M] <= 1'b0;
+ else
+ m_axi_bvalid[M] <= M_AXI_AWVALID[M];
+
+ initial m_axi_rvalid[M] <= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_rvalid[M] <= 1'b0;
+ else
+ m_axi_rvalid[M] <= M_AXI_ARVALID[M];
+
+ always @(*)
+ assert(fm_axi_awr_outstanding[M] == fm_axi_wr_outstanding[M]);
+
+ always @(*)
+ assert(fm_axi_wr_outstanding[M]== (m_axi_bvalid[M] ? 1:0));
+
+ always @(*)
+ assert(fm_axi_rd_outstanding[M]== (m_axi_rvalid[M] ? 1:0));
+ end endgenerate
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Properties necessary to pass induction
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ assert(S_AXI_WREADY == (wdecode != 0));
+`ifdef VERIFIC
+ always @(*)
+ assert($onehot0(M_AXI_AWVALID));
+
+ always @(*)
+ assert($onehot0(m_axi_bvalid));
+
+ always @(*)
+ assert($onehot0(m_axi_rvalid));
+`endif
+ always @(*)
+ begin
+ count_awr_outstanding = 0;
+ if (!S_AXI_AWREADY)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_wready)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_bvalid)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_response)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ count_awr_outstanding = count_awr_outstanding + bfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_awr_outstanding == count_awr_outstanding);
+
+ always @(*)
+ begin
+ count_wr_outstanding = 0;
+ if (write_bvalid)
+ count_wr_outstanding = count_wr_outstanding + 1;
+ if (write_response)
+ count_wr_outstanding = count_wr_outstanding + 1;
+ count_wr_outstanding = count_wr_outstanding + bfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_wr_outstanding == count_wr_outstanding);
+
+ //
+ //
+ //
+ always @(*)
+ begin
+ count_rd_outstanding = 0;
+ if (read_rwait)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ if (read_rvalid)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ if (read_result)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ count_rd_outstanding = count_rd_outstanding + rfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_rd_outstanding == count_rd_outstanding);
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [3:0] cvr_arvalids, cvr_awvalids, cvr_reads, cvr_writes;
+
+ initial cvr_awvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_awvalids <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && !(&cvr_awvalids))
+ cvr_awvalids <= cvr_awvalids + 1;
+
+ initial cvr_arvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_arvalids <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && !(&cvr_arvalids))
+ cvr_arvalids <= cvr_arvalids + 1;
+
+ initial cvr_writes = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_writes <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY && !(&cvr_writes))
+ cvr_writes <= cvr_writes + 1;
+
+ initial cvr_reads = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_reads <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && !(&cvr_arvalids))
+ cvr_reads <= cvr_reads + 1;
+
+ always @(*)
+ cover(cvr_awvalids > 4);
+
+ always @(*)
+ cover(cvr_arvalids > 4);
+
+ always @(*)
+ cover(cvr_reads > 4);
+
+ always @(*)
+ cover(cvr_writes > 4);
+
+ always @(*)
+ cover((cvr_writes > 4) && (cvr_reads > 4));
+`endif
+endmodule
+// `ifndef YOSYS
+// `default_nettype wire
+// `endif
diff --git a/rtl/wb2axip/axilempty.v b/rtl/wb2axip/axilempty.v
new file mode 100644
index 0000000..f429d69
--- /dev/null
+++ b/rtl/wb2axip/axilempty.v
@@ -0,0 +1,371 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilempty.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Modifies the simple AXI-lite interface to be an empty shell
+//
+// This is useful for a bus with masters but no slaves. When used,
+// the interconnect can connect those masters to this slave to know
+// that requests will still be properly handled--and get proper error
+// returns.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilempty #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ // Verilator lint_off UNUSED
+ parameter C_AXI_ADDR_WIDTH = 4,
+ // Verilator lint_on UNUSED
+ localparam C_AXI_DATA_WIDTH = 32,
+ parameter [0:0] OPT_SKIDBUFFER = 1'b0,
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ //
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ reg axil_read_valid;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_WRITE
+
+ wire awskd_valid, wskd_valid, awskd_unused, wskd_unused;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER), .DW(1))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(1'b0),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_unused));
+
+`ifdef FORMAL
+ always @(*)
+ if (awskd_valid)
+ assert(awskd_unused == 0);
+`endif
+
+ skidbuffer #(.OPT_OUTREG(0), .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(1))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ 1'b0 }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data(wskd_unused));
+`ifdef FORMAL
+ always @(*)
+ if (wskd_valid)
+ assert(wskd_unused == 0);
+`endif
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, awskd_unused, wskd_unused };
+ // Verilator lint_on UNUSED
+ end else begin : SIMPLE_WRITES
+
+ reg axil_awready;
+
+ initial axil_awready = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axil_awready <= 1'b0;
+ else
+ axil_awready <= !axil_awready
+ && (S_AXI_AWVALID && S_AXI_WVALID)
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ assign S_AXI_AWREADY = axil_awready;
+ assign S_AXI_WREADY = axil_awready;
+
+ assign axil_write_ready = axil_awready;
+
+ end endgenerate
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+ assign S_AXI_BRESP = 2'b11;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_READ
+
+ wire arskd_valid, arskd_unused;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(1))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data( 1'b0 ),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_unused));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXI_RREADY);
+
+`ifdef FORMAL
+ always @(*)
+ if (arskd_valid)
+ assert(arskd_unused == 0);
+`endif
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, arskd_unused };
+ // Verilator lint_on UNUSED
+ end else begin : SIMPLE_READS
+
+ reg axil_arready;
+
+ always @(*)
+ axil_arready = !S_AXI_RVALID;
+
+ assign S_AXI_ARREADY = axil_arready;
+ assign axil_read_ready = (S_AXI_ARVALID && S_AXI_ARREADY);
+
+ end endgenerate
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+ assign S_AXI_RDATA = 0;
+ assign S_AXI_RRESP = 2'b11;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+
+ // }}}
+
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal properties used in verfiying this core
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3),
+ .F_OPT_COVER_BURST(0)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr({(C_AXI_ADDR_WIDTH){1'b0}}),
+ .i_axi_awprot( 3'h0),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( {(C_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_wstrb( {(C_AXI_DATA_WIDTH/8){1'b0}}),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( {(C_AXI_ADDR_WIDTH){1'b0}}),
+ .i_axi_arprot( 3'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)
+ // }}}
+ );
+
+ always @(*)
+ if (OPT_SKIDBUFFER)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end else begin
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0));
+ assert(faxil_awr_outstanding == faxil_wr_outstanding);
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ assert(S_AXI_RDATA == 0);
+ always @(*)
+ assert(S_AXI_RRESP == 2'b11);
+ always @(*)
+ assert(S_AXI_BRESP == 2'b11);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // While there are already cover properties in the formal property
+ // set above, you'll probably still want to cover something
+ // application specific here
+
+ // }}}
+ // }}}
+`endif
+endmodule
diff --git a/rtl/wb2axip/axilfetch.v b/rtl/wb2axip/axilfetch.v
new file mode 100644
index 0000000..7089b20
--- /dev/null
+++ b/rtl/wb2axip/axilfetch.v
@@ -0,0 +1,469 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilfetch.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: This is a very simple instruction fetch approach based around
+// AXI-lite.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilfetch #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 64,
+ parameter INSN_WIDTH=32,
+ parameter FETCH_LIMIT=16,
+ parameter [0:0] SWAP_ENDIANNESS = 1'b1,
+ localparam AW=C_AXI_ADDR_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // CPU interaction wires
+ // {{{
+ input wire i_cpu_reset,
+ input wire i_new_pc,
+ input wire i_clear_cache,
+ input wire i_ready,
+ input wire [AW-1:0] i_pc, // Ignd unls i_new_pc
+ output wire [INSN_WIDTH-1:0] o_insn, // Insn read from bus
+ output reg [AW-1:0] o_pc, // Addr of that insn
+ output reg o_valid, // If valid
+ output reg o_illegal, // Bus error
+ // }}}
+ // AXI-lite bus interface
+ // {{{
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output reg [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [2:0] M_AXI_ARPROT,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ // }}}
+ );
+
+ // Declarations
+ // {{{
+ localparam AXILLSB = $clog2(C_AXI_DATA_WIDTH/8);
+ localparam INSNS_PER_WORD = C_AXI_DATA_WIDTH / INSN_WIDTH;
+ localparam INSN_LSB = $clog2(INSN_WIDTH/8);
+ localparam LGDEPTH = $clog2(FETCH_LIMIT)+4;
+ localparam LGFIFO = $clog2(FETCH_LIMIT);
+ localparam W = LGDEPTH;
+ localparam FILLBITS = $clog2(INSNS_PER_WORD);
+ // ($clog2(INSNS_PER_WORD) > 0)
+ // ? $clog2(INSNS_PER_WORD) : 1);
+
+ reg [W:0] new_flushcount, outstanding,
+ next_outstanding, flushcount;
+ reg flushing, flush_request, full_bus;
+ wire [((AXILLSB>INSN_LSB) ? (AXILLSB-INSN_LSB-1):0):0] shift;
+ wire fifo_reset, fifo_wr, fifo_rd;
+ wire ign_fifo_full, fifo_empty;
+ wire [LGFIFO:0] ign_fifo_fill;
+ wire [C_AXI_DATA_WIDTH:0] fifo_data;
+ reg pending_new_pc;
+ reg [C_AXI_ADDR_WIDTH-1:0] pending_pc;
+ reg [W-1:0] fill;
+ reg [FILLBITS:0] out_fill;
+ reg [C_AXI_DATA_WIDTH-1:0] out_data;
+ reg [C_AXI_DATA_WIDTH-1:0] endian_swapped_rdata;
+ // }}}
+
+ assign fifo_reset = i_cpu_reset || i_clear_cache || i_new_pc;
+ assign fifo_wr = M_AXI_RVALID && !flushing;
+
+
+ // ARPROT = 3'b100 for an unprivileged, secure instruction access
+ // (not sure what unprivileged or secure mean--even after reading the
+ // spec)
+ assign M_AXI_ARPROT = 3'b100;
+
+ // next_outstanding
+ // {{{
+ always @(*)
+ begin
+ next_outstanding = outstanding;
+
+ case({ M_AXI_ARVALID && M_AXI_ARREADY, M_AXI_RVALID })
+ 2'b10: next_outstanding = outstanding + 1;
+ 2'b01: next_outstanding = outstanding - 1;
+ default: begin end
+ endcase
+ end
+ // }}}
+
+ // outstanding, full_bus
+ // {{{
+ initial outstanding = 0;
+ initial full_bus = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ outstanding <= 0;
+ full_bus <= 0;
+ end else begin
+ outstanding <= next_outstanding;
+ full_bus <= (next_outstanding
+ // + (((M_AXI_ARVALID && !M_AXI_ARREADY) ? 1:0)
+ >= (1<<LGDEPTH)-1);
+ end
+ // }}}
+
+ // fill
+ // {{{
+ initial fill = 0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ fill <= 0;
+ // else if (fo_reset || flushing)
+ // fill <= 0;
+ else case({ M_AXI_ARVALID && M_AXI_ARREADY && !flush_request,
+ fifo_rd && !fifo_empty })
+ 2'b10: fill <= fill + 1;
+ 2'b01: fill <= fill - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // new_flushcount
+ // {{{
+ always @(*)
+ new_flushcount = outstanding + (M_AXI_ARVALID ? 1:0)
+ - (M_AXI_RVALID ? 1:0);
+ // }}}
+
+ // flushcount, flushing, flush_request
+ // {{{
+ initial flushcount = 0;
+ initial flushing = 0;
+ initial flush_request = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ flushcount <= 0;
+ flushing <= 0;
+ flush_request <= 0;
+ end else if (fifo_reset)
+ begin
+ flushcount <= new_flushcount;
+ flushing <= (new_flushcount > 0);
+ flush_request <= (M_AXI_ARVALID && !M_AXI_ARREADY);
+ end else begin
+ if (M_AXI_RVALID && flushcount > 0)
+ begin
+ flushcount <= flushcount - 1;
+ // Verilator lint_off CMPCONST
+ flushing <= (flushcount > 1);
+ // Verilator lint_on CMPCONST
+ end
+
+ if (M_AXI_ARREADY)
+ flush_request <= 0;
+ end
+ // }}}
+
+ // M_AXI_ARVALID
+ // {{{
+ initial M_AXI_ARVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_ARVALID <= 1'b0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ M_AXI_ARVALID <= 1;
+ if (i_new_pc || pending_new_pc)
+ M_AXI_ARVALID <= 1'b1;
+
+ //
+ // Throttle the number of requests we make
+ // Verilator lint_off CMPCONST
+ // Verilator lint_off WIDTH
+ // out_fill will only capture 0 or 1 if DATA_WIDTH == 32
+ if (fill + (M_AXI_ARVALID ? 1:0)
+ + ((o_valid &&(!i_ready || out_fill > 1)) ? 1:0)
+ >= FETCH_LIMIT)
+ M_AXI_ARVALID <= 1'b0;
+ // Verilator lint_on WIDTH
+ // Verilator lint_on CMPCONST
+ if (i_cpu_reset || i_clear_cache || full_bus)
+ M_AXI_ARVALID <= 1'b0;
+ end
+ // }}}
+
+ assign M_AXI_RREADY = 1'b1;
+
+ // pending_new_pc
+ // {{{
+ initial pending_new_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || i_clear_cache)
+ pending_new_pc <= 1'b0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ pending_new_pc <= 1'b0;
+ else if (i_new_pc)
+ pending_new_pc <= 1'b1;
+ // }}}
+
+ // pending_pc
+ // {{{
+ initial pending_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_new_pc)
+ pending_pc <= i_pc;
+ // }}}
+
+ // M_AXI_ARADDR
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ if (i_new_pc)
+ M_AXI_ARADDR <= i_pc;
+ else if (pending_new_pc)
+ M_AXI_ARADDR <= pending_pc;
+ else if (M_AXI_ARVALID)
+ begin
+ M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:AXILLSB]
+ <= M_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:AXILLSB] +1;
+ M_AXI_ARADDR[AXILLSB-1:0] <= 0;
+ end
+ end
+ // }}}
+
+ // o_pc
+ // {{{
+ initial o_pc = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_new_pc)
+ o_pc <= i_pc;
+ else if (o_valid && i_ready && !o_illegal)
+ begin
+ o_pc <= 0;
+ o_pc[AW-1:INSN_LSB] <= o_pc[AW-1:INSN_LSB] + 1;
+ end
+ // }}}
+
+ generate if (AXILLSB > INSN_LSB)
+ begin : BIG_WORD
+ // {{{
+ assign shift = o_pc[AXILLSB-1:INSN_LSB];
+ // }}}
+ end else begin : NO_SHIFT
+ // {{{
+ assign shift = 0;
+ // }}}
+ end endgenerate
+
+ generate if (SWAP_ENDIANNESS)
+ begin : SWAPPED_ENDIANNESS
+ // {{{
+ genvar gw, gb; // Word count, byte count
+
+ for(gw=0; gw<C_AXI_DATA_WIDTH/INSN_WIDTH; gw=gw+1) // For each bus word
+ for(gb=0; gb<(INSN_WIDTH/8); gb=gb+1) // For each bus byte
+ always @(*)
+ endian_swapped_rdata[gw*INSN_WIDTH
+ + ((INSN_WIDTH/8)-1-gb)*8 +: 8]
+ = M_AXI_RDATA[gw*INSN_WIDTH+gb*8 +: 8];
+ // }}}
+ end else begin : NO_ENDIAN_SWAP
+ // {{{
+ always @(*)
+ endian_swapped_rdata = M_AXI_RDATA;
+ // }}}
+ end endgenerate
+
+ generate if (FETCH_LIMIT <= 1)
+ begin : NOCACHE
+ // {{{
+ // No cache
+
+ // assign fifo_rd = fifo_wr;
+ // Verilator lint_off CMPCONST
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ // Verilator lint_on CMPCONST
+ assign fifo_empty = !fifo_wr; //(out_fill <= (i_aready ? 1:0));
+ assign fifo_data = { M_AXI_RRESP[1], endian_swapped_rdata };
+
+ assign ign_fifo_fill = 1'b0;
+ assign ign_fifo_full = 1'b0;
+`ifdef FORMAL
+ always @(*)
+ if (M_AXI_RVALID || M_AXI_ARVALID || outstanding > 0)
+ assert(!o_valid);
+`endif
+ // }}}
+ end else if (FETCH_LIMIT == 2)
+ begin : DBLFETCH
+ // {{{
+ // Single word cache
+ reg cache_valid;
+ reg [C_AXI_DATA_WIDTH:0] cache_data;
+
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ assign fifo_empty =(!M_AXI_RVALID && !cache_valid) || flushing;
+ assign fifo_data = cache_valid ? cache_data
+ : ({ M_AXI_RRESP[1], endian_swapped_rdata });
+
+
+ assign ign_fifo_fill = cache_valid ? 1 : 0;
+ assign ign_fifo_full = cache_valid;
+
+ initial cache_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ cache_valid <= 1'b0;
+ else if (M_AXI_RVALID && o_valid && !fifo_rd)
+ cache_valid <= 1;
+ else if (fifo_rd)
+ cache_valid <= 1'b0;
+
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_RVALID)
+ cache_data <= { M_AXI_RRESP[1], endian_swapped_rdata };
+
+ // Make Verilator happy
+ // {{{
+ wire unused_dblfetch;
+ assign unused_dblfetch = &{ 1'b0, fifo_wr };
+ // }}}
+ // }}}
+ end else begin : FIFO_FETCH
+ // {{{
+ // FIFO cache
+
+ // Verilator lint_off CMPCONST
+ // out_fill will only capture 0 or 1 if DATA_WIDTH == 32
+ assign fifo_rd = !o_valid || (i_ready && (out_fill <= 1));
+ // Verilator lint_on CMPCONST
+
+ sfifo #(
+ // {{{
+ .BW(1+C_AXI_DATA_WIDTH), .LGFLEN(LGFIFO)
+ // }}}
+ ) fcache(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(fifo_reset),
+ .i_wr(fifo_wr),
+ .i_data({M_AXI_RRESP[1], endian_swapped_rdata }),
+ .o_full(ign_fifo_full), .o_fill(ign_fifo_fill),
+ .i_rd(fifo_rd),.o_data(fifo_data),.o_empty(fifo_empty)
+ // }}}
+ );
+ // }}}
+ end endgenerate
+
+ // o_valid
+ // {{{
+ initial o_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ o_valid <= 1'b0;
+ else if (!o_valid || i_ready)
+ o_valid <= (fifo_rd && !fifo_empty)
+ || out_fill > (o_valid ? 1:0);
+ // }}}
+
+ // out_fill
+ // {{{
+ // == number of instructions in the fifo_data word that have not (yet)
+ // been accepted by the CPU.
+ // == 0 when no data is available
+ // == INSN_PER_WORD on the first instruction of any word
+ // == 1 on the last instruction of any word
+ initial out_fill = 0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ out_fill <= 0;
+ else if (fifo_rd)
+ begin
+ if (fifo_empty)
+ out_fill <= 0;
+ else if (o_valid)
+ out_fill <= INSNS_PER_WORD[FILLBITS:0];
+ else
+ // Verilator lint_off WIDTH
+ out_fill <= (INSNS_PER_WORD[FILLBITS:0] - shift);
+ // Verilator lint_on WIDTH
+ end else if (i_ready && out_fill > 0)
+ out_fill <= out_fill - 1;
+ // }}}
+
+ // out_data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (fifo_rd)
+ begin
+ if (o_valid || (INSN_WIDTH == C_AXI_DATA_WIDTH))
+ out_data <= fifo_data[C_AXI_DATA_WIDTH-1:0];
+ else
+ out_data <= fifo_data[C_AXI_DATA_WIDTH-1:0]>>(INSN_WIDTH*shift);
+ end else if (i_ready)
+ out_data <= out_data >> INSN_WIDTH;
+ // }}}
+
+ assign o_insn = out_data[INSN_WIDTH-1:0];
+
+ // o_illegal
+ // {{{
+ initial o_illegal = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (fifo_reset)
+ o_illegal <= 1'b0;
+ else if (!o_illegal && fifo_rd && !fifo_empty)
+ o_illegal <= fifo_data[C_AXI_DATA_WIDTH];
+ // }}}
+
+ // Make verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = & { 1'b0, M_AXI_RRESP[0], ign_fifo_full, ign_fifo_fill };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Formal properties for this module are maintained elsewhere
+`endif
+endmodule
diff --git a/rtl/wb2axip/axilgpio.v b/rtl/wb2axip/axilgpio.v
new file mode 100644
index 0000000..0a6e90c
--- /dev/null
+++ b/rtl/wb2axip/axilgpio.v
@@ -0,0 +1,808 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilgpio
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A simple and basic AXI-lite input and output module.
+// Tristates are not supported internally, although output bits
+// may be used externally to create a tristate input. For example, when
+// driving an I2C controller, you might wish to do something like:
+//
+// assign i2c_scl = gpio_output[1] ? 1'bz : gpio_output[1];
+// assign i2c_sda = gpio_output[0] ? 1'bz : gpio_output[0];
+//
+// Or, as another example:
+//
+// assign generic_io = gpio_output[3] ? 1'bz : gpio_output[2];
+//
+// Registers:
+// 0: LOAD
+// Write to this register will overwrite the output data bits.
+// 4: SET
+// Writes to this register will set every output data bit where
+// the written bit is set.
+//
+// OUTPUT[k] = OLD BIT[k] || NEW_BIT[k]
+//
+// 8: CLEAR
+// Writes to this register will clear every output data bit where
+// the written bit is set.
+//
+// OUTPUT[k] = OLD BIT[k] && (!NEW_BIT[k])
+//
+// 12: TOGGLE
+// Writes to this register will toggle every output bit where
+// the bit written is set.
+//
+// OUTPUT[k] = OLD BIT[k] ^ NEW_BIT[k]
+//
+// The next four registers are present if (and only if) NIN > 0. If not,
+// the prior four registers will be repeated.
+//
+// 16: Input data (if NIN > 0)
+// This is the input data, following a two-register CDC.
+// 20: Input data toggle detection
+// A bit will be set in this register if ever the associated
+// input data bit toggles. To clear, write a '1' to the toggled
+// bit in this register.
+//
+// Bits from this register that are set will then create an
+// outgoing interrupt--provided they are not masked.
+//
+// 24: Input data interrupt mask
+// If any "mask" bit is set, then toggled data will not trigger
+// an interrupt.
+// 28: Input data interrupts active
+// This is the AND of toggled data and a clear interrupt mask bit.
+//
+// An output interrupt is generated if any of the bits in the interrupt
+// active register is high.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2021-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilgpio #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ parameter C_AXI_ADDR_WIDTH = 5,
+ localparam C_AXI_DATA_WIDTH = 32,
+ // OPT_SKIDBUFFER will increase throughput to 100% from 50%
+ parameter [0:0] OPT_SKIDBUFFER = 1'b1,
+ // OPT_LOWPOWER will force RDATA to zero if ever !RVALID
+ parameter [0:0] OPT_LOWPOWER = 0,
+ // NOUT : Number of output bits. Must be > 0
+ parameter NOUT = 30,
+ // NIN: Number of input bits. May be zero if desired.
+ parameter NIN = 5,
+ parameter [NOUT-1:0] DEFAULT_OUTPUT = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI-lite interface
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ // }}}
+ output wire [NOUT-1:0] o_gpio,
+ input wire [((NIN>0) ? (NIN-1):0):0] i_gpio,
+ output wire o_int
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [2:0] // ADDR_LOAD = 3'b000,
+ // ADDR_SET = 3'b001,
+ // ADDR_CLEAR = 3'b010,
+ // ADDR_TOGGLE = 3'b011,
+ ADDR_INDATA = 3'b100,
+ ADDR_CHANGED= 3'b101,
+ ADDR_MASK = 3'b110,
+ ADDR_INT = 3'b111;
+
+
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr;
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ reg [31:0] r_gpio;
+ wire [31:0] ck_gpio, ck_toggled, w_mask, int_toggled, wskd_gpio;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_WRITE
+ // {{{
+ wire awskd_valid, wskd_valid;
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8)
+ // }}}
+ ) axilwskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+ // }}}
+ end else begin : SIMPLE_WRITES
+ // {{{
+ reg axil_awready;
+
+ initial axil_awready = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axil_awready <= 1'b0;
+ else
+ axil_awready <= !axil_awready
+ && (S_AXI_AWVALID && S_AXI_WVALID)
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ assign S_AXI_AWREADY = axil_awready;
+ assign S_AXI_WREADY = axil_awready;
+
+ assign awskd_addr = S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign wskd_data = S_AXI_WDATA;
+ assign wskd_strb = S_AXI_WSTRB;
+
+ assign axil_write_ready = axil_awready;
+ // }}}
+ end endgenerate
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+ assign S_AXI_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_READ
+ // {{{
+ wire arskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXI_RREADY);
+ // }}}
+ end else begin : SIMPLE_READS
+ // {{{
+ reg axil_arready;
+
+ always @(*)
+ axil_arready = !S_AXI_RVALID;
+
+ assign arskd_addr = S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign S_AXI_ARREADY = axil_arready;
+ assign axil_read_ready = (S_AXI_ARVALID && S_AXI_ARREADY);
+ // }}}
+ end endgenerate
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+ assign S_AXI_RDATA = axil_read_data;
+ assign S_AXI_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ assign wskd_gpio = apply_wstrb(r_gpio, wskd_data, wskd_strb);
+
+ // r_gpio, o_gpio
+ // {{{
+ initial r_gpio = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (axil_write_ready)
+ begin
+ case({ (awskd_addr[2] && (NIN > 0)), awskd_addr[1:0] })
+ 3'b000: r_gpio <= wskd_gpio;
+ 3'b001: begin // SET
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<= r_gpio[ 7: 0]| wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<= r_gpio[15: 8]| wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<= r_gpio[23:16]| wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<= r_gpio[31:24]| wskd_data[31:24];
+ end
+ // }}}
+ 3'b010: begin // CLEAR
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<= r_gpio[ 7: 0]&~wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<= r_gpio[15: 8]&~wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<= r_gpio[23:16]&~wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<= r_gpio[31:24]&~wskd_data[31:24];
+ end
+ // }}}
+ 3'b011: begin // TOGGLE
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<=r_gpio[ 7: 0] ^ wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<=r_gpio[15: 8] ^ wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<=r_gpio[23:16] ^ wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<=r_gpio[31:24] ^ wskd_data[31:24];
+ end
+ // }}}
+ default: begin end // Input registers
+ endcase
+ end
+
+ if (i_reset)
+ r_gpio[NOUT-1:0] <= DEFAULT_OUTPUT;
+ if (NOUT < 32)
+ r_gpio[31:((NOUT < 32) ? NOUT : 31)] <= 0;
+ end
+
+ assign o_gpio = r_gpio[NOUT-1:0];
+ // }}}
+
+ // i_gpio -> ck_gpio, $changed(i_gpio) -> ck_toggled, r_mask
+ // {{{
+ generate if (NIN > 0)
+ begin : INPUT_HANDLING
+ // {{{
+ // Poss interrupts: Toggle, Rise, Fall
+ // Only toggle is implemented here
+ reg [NIN-1:0] last_gpio, qq_gpio, q_gpio, toggled,
+ r_mask;
+ wire [31:0] wstrb_mask;
+ reg r_int;
+ integer ik;
+
+ // r_int
+ // {{{
+ initial r_int = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ r_int <= 0;
+ else
+ r_int <= |(r_mask & toggled);
+ // }}}
+
+ // Two clock CDC: last_gpio, qq_gpio, q_gpio
+ // {{{
+ initial { last_gpio, qq_gpio, q_gpio } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ { last_gpio, qq_gpio, q_gpio } <= 0;
+ else
+ { last_gpio, qq_gpio, q_gpio }
+ <= { qq_gpio, q_gpio, i_gpio };
+ // }}}
+
+ // Toggled
+ // {{{
+ initial toggled = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ toggled <= 0;
+ else begin
+ for(ik=0; ik<NIN; ik=ik+1)
+ begin
+ if (axil_write_ready && awskd_addr == 3'b101
+ && wskd_strb[ik/8] && wskd_data[ik])
+ toggled[ik] <= 1'b0;
+ if (last_gpio[ik] ^ qq_gpio[ik])
+ toggled[ik] <= 1'b1;
+ end
+ end
+ // }}}
+
+ // r_mask
+ // {{{
+ assign wstrb_mask = apply_wstrb(w_mask, wskd_data, wskd_strb);
+
+ initial r_mask = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ r_mask <= 0;
+ else if (axil_write_ready && awskd_addr == 3'b110)
+ r_mask <= wstrb_mask[NIN-1:0];
+ // }}}
+
+ assign ck_gpio = { {(32-NIN){1'b0}}, qq_gpio };
+ assign ck_toggled = { {(32-NIN){1'b0}}, toggled };
+ assign w_mask = { {(32-NIN){1'b0}}, r_mask };
+ assign int_toggled = { {(32-NIN){1'b0}}, (r_mask & toggled) };
+ assign o_int = r_int;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_inputs;
+ assign unused_inputs = &{ 1'b0, wstrb_mask[31:NIN] };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+ end else begin : NO_INPUTS
+ // {{{
+ assign ck_gpio = 32'h0;
+ assign ck_toggled = 32'h0;
+ assign w_mask = 32'h0;
+ assign int_toggled = 32'h0;
+ assign o_int = 1'b0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_inputs;
+ assign unused_inputs = &{ 1'b0, i_gpio };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+
+ // axil_read_data
+ // {{{
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ axil_read_data <= 0;
+
+ casez({ ((NIN>0)&& arskd_addr[2]), arskd_addr[1:0] })
+ 3'b0??: axil_read_data[NOUT-1:0] <= r_gpio[NOUT-1:0];
+ ADDR_INDATA: axil_read_data <= ck_gpio;
+ ADDR_CHANGED: axil_read_data <= ck_toggled;
+ ADDR_MASK: axil_read_data <= w_mask;
+ ADDR_INT: axil_read_data <= int_toggled;
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+ // }}}
+
+ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXI_DATA_WIDTH-1:0] prior_data;
+ input [C_AXI_DATA_WIDTH-1:0] new_data;
+ input [C_AXI_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_ARADDR[ADDRLSB-1:0],
+ S_AXI_AWADDR[ADDRLSB-1:0] };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(3),
+ .F_AXI_MAXDELAY(3),
+ .F_AXI_MAXRSTALL(5),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .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)
+ // }}}
+ );
+
+ always @(*)
+ if (OPT_SKIDBUFFER)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end else begin
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0));
+ assert(faxil_awr_outstanding == faxil_wr_outstanding);
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXI_RVALID)
+ assert(S_AXI_RDATA == 0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register return checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`define CHECK_REGISTERS
+`ifdef CHECK_REGISTERS
+ (* anyconst *) reg [$clog2(NOUT)-1:0] f_obit;
+
+ always @(*)
+ assume(f_obit < NOUT);
+
+ // Verify o_gpio
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if ($past(i_reset) || i_reset)
+ begin
+ if ($past(i_reset))
+ assert(o_gpio[f_obit] == DEFAULT_OUTPUT[f_obit]);
+ end else if ($past(axil_write_ready && wskd_strb[f_obit/8]))
+ begin
+ case($past(awskd_addr[2:0]))
+ 3'b000: assert(o_gpio[f_obit] == $past(wskd_data[f_obit]));
+ 3'b001: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] || wskd_data[f_obit]));
+ 3'b010: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] && !wskd_data[f_obit]));
+ 3'b011: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] ^ wskd_data[f_obit]));
+ default: begin end
+ endcase
+ end
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ 3'b000, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) foutputs (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr[2], {(ADDRLSB+2){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr[2], {(ADDRLSB+2){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(o_gpio)
+ // }}}
+ );
+
+ // }}}
+
+ generate if (NIN > 0)
+ begin : CHECK_INPUT
+ (* anyconst *) reg [$clog2(NIN)-1:0] f_ibit;
+
+ always @(*)
+ assume(f_ibit < NIN);
+
+ always @(posedge S_AXI_ACLK)
+ if ($past(i_reset) || i_reset)
+ begin
+ if ($past(i_reset))
+ assert(!ck_gpio[f_ibit]);
+ end else begin
+ if (!$past(i_reset, 2)
+ && $past(ck_gpio[f_ibit]) != $past(ck_gpio[f_ibit],2))
+ assert(ck_toggled[f_ibit]);
+ else if ($past(axil_write_ready
+ && awskd_addr == ADDR_CHANGED
+ && wskd_strb[f_ibit/8]
+ && wskd_data[f_ibit]))
+ assert(!ck_toggled[f_ibit]);
+ end
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_INDATA, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) finputs (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(ck_gpio)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_CHANGED, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) ftoggled (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(ck_toggled)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_MASK, {(ADDRLSB){1'b0}} }),
+ .MASK({ {(C_AXI_DATA_WIDTH-NIN){1'b0}}, {(NIN){1'b1}} })
+ // }}}
+ ) fmask (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(w_mask)
+ // }}}
+ );
+ end endgenerate
+`endif
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (!i_reset)
+ cover(o_int);
+
+ always @(posedge S_AXI_ACLK)
+ if (!i_reset)
+ cover($fell(o_int));
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axilite2axi.v b/rtl/wb2axip/axilite2axi.v
new file mode 100644
index 0000000..8b8adf3
--- /dev/null
+++ b/rtl/wb2axip/axilite2axi.v
@@ -0,0 +1,374 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilite2axi.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose:
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilite2axi #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 4,
+ C_AXI_ADDR_WIDTH= 32,
+ C_AXI_DATA_WIDTH= 32,
+ parameter [C_AXI_ID_WIDTH-1:0] C_AXI_WRITE_ID = 0,
+ C_AXI_READ_ID = 0
+ // }}}
+ ) (
+ // {{{
+ input wire ACLK,
+ input wire ARESETN,
+ // Slave AXI interface
+ // {{{
+ // Slave write signals
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [3-1:0] S_AXI_AWPROT,
+ // Slave write data signals
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ // Slave return write response
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [2-1:0] S_AXI_BRESP,
+ // Slave read signals
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [3-1:0] S_AXI_ARPROT,
+ // Slave read data signals
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [2-1:0] S_AXI_RRESP,
+ // }}}
+ // Master AXI interface
+ // {{{
+ // Master interface write address
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [8-1:0] M_AXI_AWLEN,
+ output wire [3-1:0] M_AXI_AWSIZE,
+ output wire [2-1:0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ output wire [4-1:0] M_AXI_AWCACHE,
+ output wire [3-1:0] M_AXI_AWPROT,
+ output wire [4-1:0] M_AXI_AWQOS,
+ // Master write data
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ // Master write response
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ // Master interface read address
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [8-1:0] M_AXI_ARLEN,
+ output wire [3-1:0] M_AXI_ARSIZE,
+ output wire [2-1:0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [4-1:0] M_AXI_ARCACHE,
+ output wire [3-1:0] M_AXI_ARPROT,
+ output wire [4-1:0] M_AXI_ARQOS,
+ // Master interface read data return
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [2-1:0] M_AXI_RRESP
+ // }}}
+ // }}}
+ );
+
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ assign M_AXI_AWID = C_AXI_WRITE_ID;
+ assign M_AXI_AWADDR = S_AXI_AWADDR;
+ assign M_AXI_AWLEN = 0;
+ assign M_AXI_AWSIZE = ADDRLSB[2:0];
+ assign M_AXI_AWBURST = 0;
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE = 4'b0011; // As recommended by Xilinx UG1037
+ assign M_AXI_AWPROT = S_AXI_AWPROT;
+ assign M_AXI_AWQOS = 0;
+ assign M_AXI_AWVALID = S_AXI_AWVALID;
+ assign S_AXI_AWREADY = M_AXI_AWREADY;
+ // Master write data
+ assign M_AXI_WDATA = S_AXI_WDATA;
+ assign M_AXI_WLAST = 1;
+ assign M_AXI_WSTRB = S_AXI_WSTRB;
+ assign M_AXI_WVALID = S_AXI_WVALID;
+ assign S_AXI_WREADY = M_AXI_WREADY;
+ // Master write response
+ assign S_AXI_BRESP = M_AXI_BRESP;
+ assign S_AXI_BVALID = M_AXI_BVALID;
+ assign M_AXI_BREADY = S_AXI_BREADY;
+ //
+ //
+ assign M_AXI_ARID = C_AXI_READ_ID;
+ assign M_AXI_ARADDR = S_AXI_ARADDR;
+ assign M_AXI_ARLEN = 0;
+ assign M_AXI_ARSIZE = ADDRLSB[2:0];
+ assign M_AXI_ARBURST = 0;
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE = 4'b0011; // As recommended by Xilinx UG1037
+ assign M_AXI_ARPROT = S_AXI_ARPROT;
+ assign M_AXI_ARQOS = 0;
+ assign M_AXI_ARVALID = S_AXI_ARVALID;
+ assign S_AXI_ARREADY = M_AXI_ARREADY;
+ //
+ assign S_AXI_RDATA = M_AXI_RDATA;
+ assign S_AXI_RRESP = M_AXI_RRESP;
+ assign S_AXI_RVALID = M_AXI_RVALID;
+ assign M_AXI_RREADY = S_AXI_RREADY;
+
+ // Make Verilator happy
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, ACLK, ARESETN, M_AXI_RLAST, M_AXI_RID, M_AXI_BID };
+ // Verilator lint_on UNUSED
+
+`ifdef FORMAL
+ //
+ // The following are some of the properties used to verify this design
+ //
+
+ localparam F_LGDEPTH = 10;
+
+ wire [F_LGDEPTH-1:0] faxil_awr_outstanding,
+ faxil_wr_outstanding, faxil_rd_outstanding;
+
+ faxil_slave #(
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .F_LGDEPTH(10),
+ .F_AXI_MAXRSTALL(4),
+ .F_AXI_MAXWAIT(9),
+ .F_AXI_MAXDELAY(0)
+ ) faxil(.i_clk(ACLK), .i_axi_reset_n(ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ //
+ .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_awr_outstanding(faxil_awr_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_rd_outstanding(faxil_rd_outstanding));
+
+ //
+ // ...
+ //
+
+ faxi_master #(
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_MAXBURST(0),
+ .OPT_EXCLUSIVE(1'b0),
+ .OPT_NARROW_BURST(1'b0),
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(10),
+ .F_AXI_MAXSTALL(4),
+ .F_AXI_MAXRSTALL(0),
+ .F_AXI_MAXDELAY(4)
+ ) faxi(.i_clk(ACLK), .i_axi_reset_n(ARESETN),
+ //
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awid( M_AXI_AWID),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awlen( M_AXI_AWLEN),
+ .i_axi_awsize( M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock( M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot( M_AXI_AWPROT),
+ .i_axi_awqos( M_AXI_AWQOS),
+ .i_axi_awvalid(M_AXI_AWVALID),
+ //
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ .i_axi_wlast( M_AXI_WLAST),
+ .i_axi_wvalid(M_AXI_WVALID),
+ //
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid( M_AXI_BID),
+ .i_axi_bresp( M_AXI_BRESP),
+ .i_axi_bvalid(M_AXI_BVALID),
+ //
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_arid( M_AXI_ARID),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arlen( M_AXI_ARLEN),
+ .i_axi_arsize( M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock( M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot( M_AXI_ARPROT),
+ .i_axi_arqos( M_AXI_ARQOS),
+ .i_axi_arvalid(M_AXI_ARVALID),
+ //
+ .i_axi_rid( M_AXI_RID),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rresp( M_AXI_RRESP),
+ .i_axi_rvalid(M_AXI_RVALID),
+ //
+ .f_axi_awr_nbursts(faxi_awr_nbursts),
+ .f_axi_wr_pending(faxi_wr_pending),
+ .f_axi_rd_nbursts(faxi_rd_nbursts),
+ .f_axi_rd_outstanding(faxi_rd_outstanding)
+ //
+ // ...
+ //
+ );
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction rule checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // First rule: the AXI solver isn't permitted to have any more write
+ // bursts outstanding than the AXI-lite interface has.
+ always @(*)
+ assert(faxi_awr_nbursts == faxil_awr_outstanding);
+
+ //
+ // Second: Since our bursts are limited to one value each, and since
+ // we can't send address bursts ahead of their data counterparts,
+ // (AXI property limitation) faxi_wr_pending can only ever be one or
+ // zero, and the counters must match
+ always @(*)
+ begin
+ //
+ // ...
+ //
+
+ if (faxil_awr_outstanding != 0)
+ begin
+ assert((faxil_awr_outstanding == faxil_wr_outstanding)
+ ||(faxil_awr_outstanding-1 == faxil_wr_outstanding));
+ end
+
+ if (faxil_awr_outstanding == 0)
+ assert(faxil_wr_outstanding == 0);
+ end
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Known "bugs"
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // These are really more limitations in the AXI properties than
+ // bugs in the code, but they need to be documented here.
+ //
+ generate if (C_AXI_DATA_WIDTH > 8)
+ begin
+ // The AXI-lite property checker doesn't validate WSTRB
+ // values like it should. If we don't force the AWADDR
+ // LSBs to zero, the solver will pick an invalid WSTRB.
+ //
+ always @(*)
+ assume(S_AXI_AWADDR[$clog2(C_AXI_DATA_WIDTH)-3:0] == 0);
+ always @(*)
+ assert(faxi_wr_addr[$clog2(C_AXI_DATA_WIDTH)-3:0] == 0);
+ end endgenerate
+
+ //
+ // The AXI solver can't handle write transactions without either a
+ // concurrent or proceeding write address burst. Here we make that
+ // plain. It's not likely to cause any problems, but still worth
+ // noting.
+ always @(*)
+ if (faxil_awr_outstanding == faxil_wr_outstanding)
+ assume(S_AXI_AWVALID || !S_AXI_WVALID);
+ else if (faxil_awr_outstanding > faxil_wr_outstanding)
+ assume(!S_AXI_AWVALID);
+
+ //
+ // We need to make certain that our counters never overflow. This is
+ // an assertion within the property checkers, so we need an appropriate
+ // matching assumption here. In practice, F_LGDEPTH could be set
+ // so arbitrarily large that this assumption would never get hit,
+ // but the solver doesn't know that.
+ always @(*)
+ if (faxil_rd_outstanding >= {{(F_LGDEPTH-1){1'b1}}, 1'b0 })
+ assume(!S_AXI_ARVALID);
+
+ always @(*)
+ if (faxil_awr_outstanding >= {{(F_LGDEPTH-1){1'b1}}, 1'b0 })
+ assume(!S_AXI_AWVALID);
+
+`endif
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axilrd2wbsp.v b/rtl/wb2axip/axilrd2wbsp.v
new file mode 100644
index 0000000..569b5e8
--- /dev/null
+++ b/rtl/wb2axip/axilrd2wbsp.v
@@ -0,0 +1,601 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilrd2wbsp.v (AXI lite to wishbone slave, read channel)
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Bridge an AXI lite read channel pair to a single wishbone read
+// channel.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axilrd2wbsp #(
+ // {{{
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ADDR_WIDTH = 28,
+ parameter AXILLSB = $clog2(C_AXI_DATA_WIDTH/8),
+ localparam AW = C_AXI_ADDR_WIDTH-AXILLSB,
+ localparam DW = C_AXI_DATA_WIDTH,
+ parameter LGFIFO = 3
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk,
+ input wire i_axi_reset_n,
+
+ // AXI read address channel signals
+ // {{{
+ input wire i_axi_arvalid,
+ output reg o_axi_arready,
+ input wire [C_AXI_ADDR_WIDTH-1:0] i_axi_araddr,
+ input wire [2:0] i_axi_arprot,
+ // }}}
+ // AXI read data channel signals
+ // {{{
+ output reg o_axi_rvalid,
+ input wire i_axi_rready,
+ output wire [C_AXI_DATA_WIDTH-1:0] o_axi_rdata,
+ output reg [1:0] o_axi_rresp,
+ // }}}
+ // Wishbone signals
+ // {{{
+ // We'll share the clock and the reset
+ output reg o_wb_cyc,
+ output reg o_wb_stb,
+ output reg [(AW-1):0] o_wb_addr,
+ output wire [(DW/8-1):0] o_wb_sel,
+ input wire i_wb_stall,
+ input wire i_wb_ack,
+ input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
+ input wire i_wb_err
+`ifdef FORMAL
+ // {{{
+ // Output connections only used in formal mode
+ , output wire [LGFIFO:0] f_first,
+ output wire [LGFIFO:0] f_mid,
+ output wire [LGFIFO:0] f_last
+ // }}}
+`endif
+ // }}}
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3;
+
+ wire w_reset;
+ assign w_reset = (!i_axi_reset_n);
+
+ reg r_stb;
+ reg [AW-1:0] r_addr;
+
+ localparam FLEN=(1<<LGFIFO);
+
+ reg [DW-1:0] dfifo [0:(FLEN-1)];
+ reg fifo_full, fifo_empty;
+
+ reg [LGFIFO:0] r_first, r_mid, r_last;
+ wire [LGFIFO:0] next_first, next_last;
+ reg wb_pending;
+ reg [LGFIFO:0] wb_outstanding;
+ wire [DW-1:0] read_data;
+ reg err_state;
+ reg [LGFIFO:0] err_loc;
+ // }}}
+
+ // o_wb_cyc, o_wb_stb
+ // {{{
+ initial o_wb_cyc = 1'b0;
+ initial o_wb_stb = 1'b0;
+ always @(posedge i_clk)
+ if ((w_reset)||((o_wb_cyc)&&(i_wb_err))||(err_state))
+ o_wb_stb <= 1'b0;
+ else if (r_stb || ((i_axi_arvalid)&&(o_axi_arready)))
+ o_wb_stb <= 1'b1;
+ else if ((o_wb_cyc)&&(!i_wb_stall))
+ o_wb_stb <= 1'b0;
+
+ always @(*)
+ o_wb_cyc = (wb_pending)||(o_wb_stb);
+ // }}}
+
+ // o_wb_addr
+ // {{{
+ always @(posedge i_clk)
+ if (r_stb && !i_wb_stall)
+ o_wb_addr <= r_addr;
+ else if ((o_axi_arready)&&((!o_wb_stb)||(!i_wb_stall)))
+ o_wb_addr <= i_axi_araddr[AW+1:AXI_LSBS];
+ // }}}
+
+ // o_wb_sel
+ // {{{
+ assign o_wb_sel = {(DW/8){1'b1}};
+ // }}}
+
+ // r_stb, r_addr
+ // {{{
+ // Shadow request
+ initial r_stb = 1'b0;
+ always @(posedge i_clk)
+ begin
+ if ((i_axi_arvalid)&&(o_axi_arready)&&(o_wb_stb)&&(i_wb_stall))
+ begin
+ r_stb <= 1'b1;
+ r_addr <= i_axi_araddr[AW+1:AXI_LSBS];
+ end else if ((!i_wb_stall)||(!o_wb_cyc))
+ r_stb <= 1'b0;
+
+ if ((w_reset)||(o_wb_cyc)&&(i_wb_err)||(err_state))
+ r_stb <= 1'b0;
+ end
+ // }}}
+
+ // wb_pending, wb_outstanding
+ // {{{
+ initial wb_pending = 0;
+ initial wb_outstanding = 0;
+ always @(posedge i_clk)
+ if ((w_reset)||(!o_wb_cyc)||(i_wb_err)||(err_state))
+ begin
+ wb_pending <= 1'b0;
+ wb_outstanding <= 0;
+ end else case({ (o_wb_stb)&&(!i_wb_stall), i_wb_ack })
+ 2'b01: begin
+ wb_outstanding <= wb_outstanding - 1'b1;
+ wb_pending <= (wb_outstanding >= 2);
+ end
+ 2'b10: begin
+ wb_outstanding <= wb_outstanding + 1'b1;
+ wb_pending <= 1'b1;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ assign next_first = r_first + 1'b1;
+ assign next_last = r_last + 1'b1;
+
+ // fifo_full, fifo_empty
+ // {{{
+ initial fifo_full = 1'b0;
+ initial fifo_empty = 1'b1;
+ always @(posedge i_clk)
+ if (w_reset)
+ begin
+ fifo_full <= 1'b0;
+ fifo_empty <= 1'b1;
+ end else case({ (o_axi_rvalid)&&(i_axi_rready),
+ (i_axi_arvalid)&&(o_axi_arready) })
+ 2'b01: begin
+ fifo_full <= (next_first[LGFIFO-1:0] == r_last[LGFIFO-1:0])
+ &&(next_first[LGFIFO]!=r_last[LGFIFO]);
+ fifo_empty <= 1'b0;
+ end
+ 2'b10: begin
+ fifo_full <= 1'b0;
+ fifo_empty <= 1'b0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // o_axi_arready
+ // {{{
+ initial o_axi_arready = 1'b1;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_arready <= 1'b1;
+ else if ((o_wb_cyc && i_wb_err) || err_state)
+ // On any error, drop the ready flag until it's been flushed
+ o_axi_arready <= 1'b0;
+ else if ((i_axi_arvalid)&&(o_axi_arready)&&(o_wb_stb)&&(i_wb_stall))
+ // On any request where we are already busy, r_stb will get
+ // set and we drop arready
+ o_axi_arready <= 1'b0;
+ else if (!o_axi_arready && o_wb_stb && i_wb_stall)
+ // If we've already stalled on o_wb_stb, remain stalled until
+ // the bus clears
+ o_axi_arready <= 1'b0;
+ else if (fifo_full && (!o_axi_rvalid || !i_axi_rready))
+ // If the FIFO is full, we must remain not ready until at
+ // least one acknowledgment is accepted
+ o_axi_arready <= 1'b0;
+ else if ( (!o_axi_rvalid || !i_axi_rready)
+ && (i_axi_arvalid && o_axi_arready))
+ o_axi_arready <= (next_first[LGFIFO-1:0] != r_last[LGFIFO-1:0])
+ ||(next_first[LGFIFO]==r_last[LGFIFO]);
+ else
+ o_axi_arready <= 1'b1;
+ // }}}
+
+ // r_first
+ // {{{
+ initial r_first = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_first <= 0;
+ else if ((i_axi_arvalid)&&(o_axi_arready))
+ r_first <= r_first + 1'b1;
+ // }}}
+
+ // r_mid
+ // {{{
+ initial r_mid = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_mid <= 0;
+ else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
+ r_mid <= r_mid + 1'b1;
+ else if ((err_state)&&(r_mid != r_first))
+ r_mid <= r_mid + 1'b1;
+ // }}}
+
+ // r_last
+ // {{{
+ initial r_last = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_last <= 0;
+ else if ((o_axi_rvalid)&&(i_axi_rready))
+ r_last <= r_last + 1'b1;
+ // }}}
+
+ // Write to dfifo on data return
+ // {{{
+ always @(posedge i_clk)
+ if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
+ dfifo[r_mid[(LGFIFO-1):0]] <= i_wb_data;
+ // }}}
+
+ // err_loc -- FIFO address of any error
+ // {{{
+ always @(posedge i_clk)
+ if ((o_wb_cyc)&&(i_wb_err))
+ err_loc <= r_mid;
+ // }}}
+
+ assign read_data = dfifo[r_last[LGFIFO-1:0]];
+ assign o_axi_rdata = read_data[DW-1:0];
+
+ // o_axi_rresp
+ // {{{
+ initial o_axi_rresp = 2'b00;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_rresp <= 0;
+ else if ((!o_axi_rvalid)||(i_axi_rready))
+ begin
+ if ((!err_state)&&((!o_wb_cyc)||(!i_wb_err)))
+ o_axi_rresp <= 2'b00;
+ else if ((!err_state)&&(o_wb_cyc)&&(i_wb_err))
+ begin
+ if (o_axi_rvalid)
+ o_axi_rresp <= (r_mid == next_last) ? 2'b10 : 2'b00;
+ else
+ o_axi_rresp <= (r_mid == r_last) ? 2'b10 : 2'b00;
+ end else if (err_state)
+ begin
+ if (next_last == err_loc)
+ o_axi_rresp <= 2'b10;
+ else if (o_axi_rresp[1])
+ o_axi_rresp <= 2'b11;
+ end else
+ o_axi_rresp <= 0;
+ end
+ // }}}
+
+ // err_state
+ // {{{
+ initial err_state = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ err_state <= 0;
+ else if (r_first == r_last)
+ err_state <= 0;
+ else if ((o_wb_cyc)&&(i_wb_err))
+ err_state <= 1'b1;
+ // }}}
+
+ // o_axi_rvalid
+ // {{{
+ initial o_axi_rvalid = 1'b0;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_rvalid <= 0;
+ else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
+ o_axi_rvalid <= 1'b1;
+ else if ((o_axi_rvalid)&&(i_axi_rready))
+ begin
+ if (err_state)
+ o_axi_rvalid <= (next_last != r_first);
+ else
+ o_axi_rvalid <= (next_last != r_mid);
+ end
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, i_axi_arprot,
+ i_axi_araddr[AXI_LSBS-1:0], fifo_empty };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Local declarations
+ // {{{
+ reg f_past_valid;
+ reg f_fifo_empty;
+ reg [LGFIFO:0] f_fifo_fill;
+ wire [LGFIFO:0] f_wb_nreqs, f_wb_nacks, f_wb_outstanding;
+ wire [LGFIFO:0] wb_fill;
+ wire [LGFIFO:0] f_axi_rd_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_awr_outstanding;
+ wire [LGFIFO:0] f_mid_minus_err, f_err_minus_last,
+ f_first_minus_err;
+ // }}}
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ always @(*)
+ begin
+ f_fifo_fill = (r_first - r_last);
+ f_fifo_empty = (f_fifo_fill == 0);
+ end
+
+ always @(*)
+ if (!f_past_valid)
+ assume(w_reset);
+
+ always @(*)
+ if (err_state)
+ assert(!o_axi_arready);
+
+ always @(*)
+ if (err_state)
+ assert((!o_wb_cyc)&&(!o_axi_arready));
+
+ always @(*)
+ if ((f_fifo_empty)&&(!w_reset))
+ assert((!fifo_full)&&(r_first == r_last)&&(r_mid == r_last));
+
+ always @(*)
+ if (fifo_full)
+ assert((!f_fifo_empty)
+ &&(r_first[LGFIFO-1:0] == r_last[LGFIFO-1:0])
+ &&(r_first[LGFIFO] != r_last[LGFIFO]));
+
+ always @(*)
+ assert(f_fifo_fill <= (1<<LGFIFO));
+
+ always @(*)
+ if (fifo_full)
+ assert(!o_axi_arready);
+ always @(*)
+ assert(fifo_full == (f_fifo_fill == (1<<LGFIFO)));
+ always @(*)
+ if (f_fifo_fill == (1<<LGFIFO))
+ assert(!o_axi_arready);
+ always @(*)
+ assert(wb_pending == (wb_outstanding != 0));
+
+ assign f_first = r_first;
+ assign f_mid = r_mid;
+ assign f_last = r_last;
+
+ fwb_master #(
+ // {{{
+ .AW(AW), .DW(DW), .F_LGDEPTH(LGFIFO+1)
+ // }}}
+ ) fwb(
+ // {{{
+ i_clk, w_reset,
+ o_wb_cyc, o_wb_stb, 1'b0, o_wb_addr, {(DW){1'b0}}, o_wb_sel,
+ i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
+ f_wb_nreqs,f_wb_nacks, f_wb_outstanding
+ // }}}
+ );
+
+ always @(*)
+ if (o_wb_cyc)
+ assert(f_wb_outstanding == wb_outstanding);
+
+ always @(*)
+ if (o_wb_cyc)
+ assert(wb_outstanding <= (1<<LGFIFO));
+
+ assign wb_fill = r_first - r_mid;
+ always @(*)
+ assert(wb_fill <= f_fifo_fill);
+ always @(*)
+ if (o_wb_stb)
+ assert(wb_outstanding+1+((r_stb)?1:0) == wb_fill);
+ always @(*)
+ if (o_wb_stb)
+ begin
+ assert(&o_wb_sel);
+ end else if (o_wb_cyc)
+ assert(wb_outstanding == wb_fill);
+
+ always @(*)
+ if (r_stb)
+ begin
+ assert(o_wb_stb);
+ assert(!o_axi_arready);
+ end
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(LGFIFO+1),
+ .F_OPT_READ_ONLY(1'b1),
+ .F_AXI_MAXWAIT(0),
+ .F_AXI_MAXDELAY(0)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
+ //
+ // AXI write address channel signals
+ .i_axi_awvalid(1'b0), .i_axi_awready(1'b0),
+ .i_axi_awaddr({(AW){1'b0}}),
+ // AXI write data channel signals
+ .i_axi_wvalid(1'b0), .i_axi_wready(1'b0), .i_axi_wdata(32'h0),
+ .i_axi_wstrb(4'h0),
+ // AXI write response channel signals
+ .i_axi_bvalid(1'b0), .i_axi_bready(1'b0), .i_axi_bresp(2'b00),
+ // AXI read address channel signals
+ .i_axi_arvalid(i_axi_arvalid), .i_axi_arready(o_axi_arready),
+ .i_axi_araddr(i_axi_araddr),.i_axi_arprot(i_axi_arprot),
+ // AXI read data channel signals
+ .i_axi_rvalid(o_axi_rvalid), .i_axi_rready(i_axi_rready),
+ .i_axi_rdata(o_axi_rdata), .i_axi_rresp(o_axi_rresp),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding),
+ .f_axi_wr_outstanding(f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ assert(f_axi_wr_outstanding == 0);
+ always @(*)
+ assert(f_axi_awr_outstanding == 0);
+ always @(*)
+ assert(f_axi_rd_outstanding == f_fifo_fill);
+
+ assign f_mid_minus_err = f_mid - err_loc;
+ assign f_err_minus_last = err_loc - f_last;
+ assign f_first_minus_err = f_first - err_loc;
+
+ always @(*)
+ if (o_axi_rvalid)
+ begin
+ if (!err_state)
+ begin
+ assert(!o_axi_rresp[1]);
+ end else if (err_loc == f_last)
+ begin
+ assert(o_axi_rresp == 2'b10);
+ end else if (f_err_minus_last < (1<<LGFIFO))
+ begin
+ assert(!o_axi_rresp[1]);
+ end else
+ assert(o_axi_rresp[1]);
+ end
+
+ always @(*)
+ if (err_state)
+ begin
+ assert(o_axi_rvalid == (r_first != r_last));
+ end else
+ assert(o_axi_rvalid == (r_mid != r_last));
+
+ always @(*)
+ if (err_state)
+ assert(f_first_minus_err <= (1<<LGFIFO));
+
+ always @(*)
+ if (err_state)
+ assert(f_first_minus_err != 0);
+
+ always @(*)
+ if (err_state)
+ assert(f_mid_minus_err <= f_first_minus_err);
+
+ always @(*)
+ if ((f_past_valid)&&(i_axi_reset_n)&&(f_axi_rd_outstanding > 0))
+ begin
+ if (err_state)
+ begin
+ assert((!o_wb_cyc)&&(f_wb_outstanding == 0));
+ end else if (!o_wb_cyc)
+ assert((o_axi_rvalid)&&(f_axi_rd_outstanding>0)
+ &&(wb_fill == 0));
+ end
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ cover(o_wb_cyc && o_wb_stb);
+
+ always @(*)
+ if (LGFIFO > 2)
+ cover(o_wb_cyc && f_wb_outstanding > 2);
+
+ always @(posedge i_clk)
+ cover(o_wb_cyc && i_wb_ack
+ && $past(o_wb_cyc && i_wb_ack)
+ && $past(o_wb_cyc && i_wb_ack,2));
+
+ // AXI covers
+ always @(*)
+ cover(o_axi_rvalid && i_axi_rready);
+
+ always @(posedge i_clk)
+ cover(i_axi_arvalid && o_axi_arready
+ && $past(i_axi_arvalid && o_axi_arready)
+ && $past(i_axi_arvalid && o_axi_arready,2));
+
+ always @(posedge i_clk)
+ cover(o_axi_rvalid && i_axi_rready
+ && $past(o_axi_rvalid && i_axi_rready)
+ && $past(o_axi_rvalid && i_axi_rready,2));
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_formal;
+ assign unused_formal = &{ 1'b0, f_wb_nreqs, f_wb_nacks };
+ // Verilator lint_on UNUSED
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axilsafety.v b/rtl/wb2axip/axilsafety.v
new file mode 100644
index 0000000..ff8b391
--- /dev/null
+++ b/rtl/wb2axip/axilsafety.v
@@ -0,0 +1,1449 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilsafety.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A AXI-Lite bus fault isolator. This core will isolate any
+// downstream AXI-liite slave faults from the upstream channel.
+// It sits as a bump in the wire between upstream and downstream channels,
+// and so it will consume two clocks--slowing down the slave, but
+// potentially allowing developer to recover in case of a fault.
+//
+// This core is configured by a couple parameters, which are key to its
+// functionality.
+//
+// OPT_TIMEOUT Set this to a number to be roughly the longest time
+// period you expect the slave to stall the bus, or likewise
+// the longest time period you expect it to wait for a response.
+// If the slave takes longer for either task, a fault will be
+// detected and reported.
+//
+// OPT_SELF_RESET If set, this will send a reset signal to the downstream
+// core so that you can attempt to restart it without reloading
+// the FPGA. If set, the o_reset signal will be used to reset
+// the downstream core.
+//
+// A second key feature of this core are the outgoing fault indicators,
+// o_write_fault and o_read_fault. If either signal is ever raised, the
+// slave has (somehow) violated protocol on either the write or the
+// read channels respectively. Such a violation may (or may not) return an
+// error upstream. For example, if the slave returns a response
+// following no requests from the master, then no error will be returned
+// up stream (doing so would be a protocol violation), but a fault will
+// still be detected. Use this line to trigger any internal logic
+// analyzers.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axilsafety #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 28,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter OPT_TIMEOUT = 12,
+ parameter MAX_DEPTH = (OPT_TIMEOUT),
+ localparam AW = C_AXI_ADDR_WIDTH,
+ localparam DW = C_AXI_DATA_WIDTH,
+ localparam LGTIMEOUT = $clog2(OPT_TIMEOUT+1),
+ localparam LGDEPTH = $clog2(MAX_DEPTH+1),
+ parameter [0:0] OPT_SELF_RESET = 1'b1,
+ parameter OPT_MIN_RESET = 16
+`ifdef FORMAL
+ , parameter [0:0] F_OPT_WRITES = 1'b1,
+ parameter [0:0] F_OPT_READS = 1'b1,
+ parameter [0:0] F_OPT_FAULTLESS = 1'b1
+`endif
+ // }}}
+ ) (
+ // {{{
+ output reg o_write_fault,
+ output reg o_read_fault,
+ //
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ output reg M_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output reg S_AXI_AWREADY,
+ input wire [AW-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output reg S_AXI_WREADY,
+ input wire [DW-1:0] S_AXI_WDATA,
+ input wire [DW/8-1:0] S_AXI_WSTRB,
+ //
+ output reg S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output reg [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output reg S_AXI_ARREADY,
+ input wire [AW-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output reg S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output reg [DW-1:0] S_AXI_RDATA,
+ output reg [1:0] S_AXI_RRESP,
+ //
+ //
+ //
+ output reg M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output reg [AW-1:0] M_AXI_AWADDR,
+ output reg [2:0] M_AXI_AWPROT,
+ //
+ output reg M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output reg [DW-1:0] M_AXI_WDATA,
+ output reg [DW/8-1:0] M_AXI_WSTRB,
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output reg [AW-1:0] M_AXI_ARADDR,
+ output reg [2:0] M_AXI_ARPROT,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [DW-1:0] M_AXI_RDATA,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ );
+
+ // localparam, wire, and register declarations
+ // {{{
+ localparam OPT_LOWPOWER = 1'b0;
+ localparam OKAY = 2'b00,
+ EXOKAY = 2'b01,
+ SLVERR = 2'b10;
+
+ reg [LGDEPTH-1:0] aw_count, w_count, r_count;
+ reg aw_zero, w_zero, r_zero,
+ aw_full, w_full, r_full,
+ aw_w_greater, w_aw_greater;
+ reg [LGDEPTH-1:0] downstream_aw_count, downstream_w_count, downstream_r_count;
+ reg downstream_aw_zero, downstream_w_zero, downstream_r_zero;
+ // downstream_aw_w_greater, downstream_w_aw_greater;
+
+ wire awskd_valid;
+ wire [2:0] awskd_prot;
+ wire [AW-1:0] awskd_addr;
+ reg awskd_ready;
+
+ wire wskd_valid;
+ wire [DW-1:0] wskd_data;
+ wire [DW/8-1:0] wskd_strb;
+ reg wskd_ready;
+
+ wire bskd_valid;
+ wire [1:0] bskd_resp;
+ reg bskd_ready;
+
+ reg last_bvalid;
+ reg [1:0] last_bdata;
+ reg last_bchanged;
+
+ wire arskd_valid;
+ wire [2:0] arskd_prot;
+ wire [AW-1:0] arskd_addr;
+ reg arskd_ready;
+
+ reg last_rvalid;
+ reg [DW+1:0] last_rdata;
+ reg last_rchanged;
+
+ wire rskd_valid;
+ wire [1:0] rskd_resp;
+ wire [DW-1:0] rskd_data;
+ reg rskd_ready;
+
+ reg [LGTIMEOUT-1:0] aw_stall_counter, w_stall_counter,
+ r_stall_counter, w_ack_timer, r_ack_timer;
+ reg aw_stall_limit, w_stall_limit, r_stall_limit,
+ w_ack_limit, r_ack_limit;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write address channel
+ // {{{
+
+ skidbuffer #(.DW(AW+3)
+ // {{{
+`ifdef FORMAL
+ , .OPT_PASSTHROUGH(1'b1)
+`endif
+ // }}}
+ ) awskd(S_AXI_ACLK, !S_AXI_ARESETN,
+ // {{{
+ S_AXI_AWVALID, S_AXI_AWREADY, { S_AXI_AWPROT, S_AXI_AWADDR },
+ awskd_valid, awskd_ready, { awskd_prot, awskd_addr});
+ // }}}
+
+ // awskd_ready
+ // {{{
+ // awskd_ready is the critical piece here, since it determines when
+ // we accept a packet from the skid buffer.
+ //
+ always @(*)
+ if (!M_AXI_ARESETN || o_write_fault)
+ // On any fault, we'll always accept a request (and return an
+ // error). We always accept a value if there are already
+ // more writes than write addresses accepted.
+ awskd_ready = (w_aw_greater)
+ ||((aw_zero)&&(!S_AXI_BVALID || S_AXI_BREADY));
+ else
+ // Otherwise, we accept if ever our counters aren't about to
+ // overflow, and there's a place downstream to accept it
+ awskd_ready = (!M_AXI_AWVALID || M_AXI_AWREADY)&& (!aw_full);
+ // }}}
+
+ // M_AXI_AWVALID
+ // {{{
+ initial M_AXI_AWVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN)
+ M_AXI_AWVALID <= 1'b0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ M_AXI_AWVALID <= awskd_valid && awskd_ready && !o_write_fault;
+ // }}}
+
+ // M_AXI_AWADDR, M_AXI_AWPROT
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_write_fault))
+ begin
+ M_AXI_AWADDR <= 0;
+ M_AXI_AWPROT <= 0;
+ end else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ M_AXI_AWADDR <= awskd_addr;
+ M_AXI_AWPROT <= awskd_prot;
+ end
+ // }}}
+ // }}}
+
+ //
+ // Write data channel
+ // {{{
+
+ skidbuffer #(.DW(DW+DW/8)
+ // {{{
+`ifdef FORMAL
+ , .OPT_PASSTHROUGH(1'b1)
+`endif
+ // }}}
+ ) wskd(S_AXI_ACLK, !S_AXI_ARESETN,
+ // {{{
+ S_AXI_WVALID, S_AXI_WREADY, { S_AXI_WDATA, S_AXI_WSTRB },
+ wskd_valid, wskd_ready, { wskd_data, wskd_strb});
+ // }}}
+
+ // wskd_ready
+ // {{{
+ // As with awskd_ready, this logic is the critical key.
+ //
+ always @(*)
+ if (!M_AXI_ARESETN || o_write_fault)
+ wskd_ready = (aw_w_greater)
+ || ((w_zero)&&(!S_AXI_BVALID || S_AXI_BREADY));
+ else
+ wskd_ready = (!M_AXI_WVALID || M_AXI_WREADY) && (!w_full);
+ // }}}
+
+ // M_AXI_WVALID
+ // {{{
+ initial M_AXI_WVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN)
+ M_AXI_WVALID <= 1'b0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ M_AXI_WVALID <= wskd_valid && wskd_ready && !o_write_fault;
+ // }}}
+
+ // M_AXI_WDATA, M_AXI_WSTRB
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_write_fault))
+ begin
+ M_AXI_WDATA <= 0;
+ M_AXI_WSTRB <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ M_AXI_WDATA <= wskd_data;
+ M_AXI_WSTRB <= (o_write_fault) ? 0 : wskd_strb;
+ end
+ // }}}
+ // }}}
+
+ //
+ // Write return channel
+ // {{{
+
+ // bskd_valid, M_AXI_BREADY, bskd_resp
+ // {{{
+`ifdef FORMAL
+ assign bskd_valid = M_AXI_BVALID;
+ assign M_AXI_BREADY= bskd_ready;
+ assign bskd_resp = M_AXI_BRESP;
+`else
+ skidbuffer #(.DW(2)
+ ) bskd(S_AXI_ACLK, !S_AXI_ARESETN || !M_AXI_ARESETN,
+ M_AXI_BVALID, M_AXI_BREADY, M_AXI_BRESP,
+ bskd_valid, bskd_ready, bskd_resp);
+`endif
+ // }}}
+
+ // bskd_ready
+ // {{{
+ always @(*)
+ if (o_write_fault)
+ bskd_ready = 1'b1;
+ else
+ bskd_ready = (!S_AXI_BVALID || S_AXI_BREADY);
+ // }}}
+
+ // S_AXI_BVALID
+ // {{{
+ initial S_AXI_BVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_BVALID <= 1'b0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ if (o_write_fault || !M_AXI_ARESETN)
+ S_AXI_BVALID <= (!S_AXI_BVALID&&(!aw_zero)&&(!w_zero));
+ else
+ S_AXI_BVALID <= (!downstream_aw_zero)
+ &&(!downstream_w_zero)&&(bskd_valid);
+ end
+ // }}}
+
+ // last_bvalid
+ // {{{
+ initial last_bvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_ARESETN || o_write_fault)
+ last_bvalid <= 1'b0;
+ else
+ last_bvalid <= (M_AXI_BVALID && !M_AXI_BREADY);
+ // }}}
+
+ // last_bdata
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_BVALID)
+ last_bdata <= M_AXI_BRESP;
+ // }}}
+
+ // last_bchanged
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ last_bchanged <= 1'b0;
+ else
+ last_bchanged <= (last_bvalid && (!M_AXI_BVALID
+ || last_bdata != M_AXI_BRESP));
+ // }}}
+
+ // S_AXI_BRESP
+ // {{{
+ initial S_AXI_BRESP = OKAY;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ if (o_write_fault)
+ S_AXI_BRESP <= SLVERR;
+ else if (bskd_resp == EXOKAY)
+ S_AXI_BRESP <= SLVERR;
+ else
+ S_AXI_BRESP <= bskd_resp;
+ end
+ // }}}
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Read address channel
+ // {{{
+
+ skidbuffer #(.DW(AW+3)
+ // {{{
+`ifdef FORMAL
+ , .OPT_PASSTHROUGH(1'b1)
+`endif
+ // }}}
+ ) arskd(S_AXI_ACLK, !S_AXI_ARESETN,
+ // {{{
+ S_AXI_ARVALID, S_AXI_ARREADY, { S_AXI_ARPROT, S_AXI_ARADDR },
+ arskd_valid, arskd_ready, { arskd_prot, arskd_addr });
+ // }}}
+
+ // arskd_ready
+ // {{{
+ always @(*)
+ if (!M_AXI_ARESETN || o_read_fault)
+ arskd_ready =((r_zero)&&(!S_AXI_RVALID || S_AXI_RREADY));
+ else
+ arskd_ready = (!M_AXI_ARVALID || M_AXI_ARREADY) && (!r_full);
+ // }}}
+
+ // M_AXI_ARVALID
+ // {{{
+ initial M_AXI_ARVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN)
+ M_AXI_ARVALID <= 1'b0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ M_AXI_ARVALID <= arskd_valid && arskd_ready && !o_read_fault;
+ // }}}
+
+ // M_AXI_ARADDR, M_AXI_ARPROT
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_read_fault))
+ begin
+ M_AXI_ARADDR <= 0;
+ M_AXI_ARPROT <= 0;
+ end else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ M_AXI_ARADDR <= arskd_addr;
+ M_AXI_ARPROT <= arskd_prot;
+ end
+ // }}}
+ // }}}
+
+ //
+ // Read data channel
+ // {{{
+
+ // rskd_valid, rskd_resp, rskd_data skid buffer
+ // {{{
+`ifdef FORMAL
+ assign rskd_valid = M_AXI_RVALID;
+ assign M_AXI_RREADY = rskd_ready;
+ assign { rskd_resp, rskd_data } = { M_AXI_RRESP, M_AXI_RDATA };
+`else
+ skidbuffer #(.DW(DW+2)
+ ) rskd(S_AXI_ACLK, !S_AXI_ARESETN || !M_AXI_ARESETN,
+ M_AXI_RVALID, M_AXI_RREADY, { M_AXI_RRESP, M_AXI_RDATA },
+ rskd_valid, rskd_ready, { rskd_resp, rskd_data });
+`endif
+ // ?}}}
+
+ // rskd_ready
+ // {{{
+ always @(*)
+ if (o_read_fault)
+ rskd_ready = 1;
+ else
+ rskd_ready = (!S_AXI_RVALID || S_AXI_RREADY);
+ // }}}
+
+ // S_AXI_RVALID
+ // {{{
+ initial S_AXI_RVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_RVALID <= 1'b0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (o_read_fault || !M_AXI_ARESETN)
+ S_AXI_RVALID <= (!S_AXI_RVALID && !r_zero)
+ || (arskd_valid && arskd_ready);
+ else
+ S_AXI_RVALID <= (!downstream_r_zero)&&(rskd_valid);
+ end
+ // }}}
+
+ // S_AXI_RDATA, S_AXI_RRESP
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ if (o_read_fault || !M_AXI_ARESETN)
+ S_AXI_RDATA <= 0;
+ else
+ S_AXI_RDATA <= rskd_data;
+
+ S_AXI_RRESP <= OKAY;
+ if (o_read_fault || rskd_resp == EXOKAY || !M_AXI_ARESETN)
+ S_AXI_RRESP <= SLVERR;
+ else if (!downstream_r_zero)
+ S_AXI_RRESP <= rskd_resp;
+ end
+ // }}}
+
+ // last_rvalid
+ // {{{
+ initial last_rvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ last_rvalid <= 1'b0;
+ else
+ last_rvalid <= (M_AXI_RVALID && !M_AXI_RREADY);
+ // }}}
+
+ // last_rdata
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_RVALID)
+ last_rdata <= { M_AXI_RRESP, M_AXI_RDATA };
+ // }}}
+
+ // last_rchanged
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ last_rchanged <= 1'b0;
+ else
+ last_rchanged <= (last_rvalid && (!M_AXI_RVALID
+ || last_rdata != { M_AXI_RRESP, M_AXI_RDATA }));
+ // }}}
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Usage counters
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write address channel
+ // {{{
+
+ initial aw_count = 0;
+ initial aw_zero = 1;
+ initial aw_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ aw_count <= 0;
+ aw_zero <= 1;
+ aw_full <= 0;
+ end else case({(awskd_valid && awskd_ready),S_AXI_BVALID&&S_AXI_BREADY})
+ 2'b10: begin
+ aw_count <= aw_count + 1;
+ aw_zero <= 0;
+ aw_full <= (aw_count == { {(LGDEPTH-1){1'b1}}, 1'b0 });
+ end
+ 2'b01: begin
+ aw_count <= aw_count - 1;
+ aw_zero <= (aw_count <= 1);
+ aw_full <= 0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Write data channel
+ // {{{
+ initial w_count = 0;
+ initial w_zero = 1;
+ initial w_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ w_count <= 0;
+ w_zero <= 1;
+ w_full <= 0;
+ end else case({(wskd_valid && wskd_ready), S_AXI_BVALID&& S_AXI_BREADY})
+ 2'b10: begin
+ w_count <= w_count + 1;
+ w_zero <= 0;
+ w_full <= (w_count == { {(LGDEPTH-1){1'b1}}, 1'b0 });
+ end
+ 2'b01: begin
+ w_count <= w_count - 1;
+ w_zero <= (w_count <= 1);
+ w_full <= 1'b0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // aw_w_greater, w_aw_greater
+ // {{{
+ initial aw_w_greater = 0;
+ initial w_aw_greater = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ aw_w_greater <= 0;
+ w_aw_greater <= 0;
+ end else case({(awskd_valid && awskd_ready),
+ (wskd_valid && wskd_ready)})
+ 2'b10: begin
+ aw_w_greater <= (aw_count + 1 > w_count);
+ w_aw_greater <= ( w_count > aw_count + 1);
+ end
+ 2'b01: begin
+ aw_w_greater <= (aw_count > w_count + 1);
+ w_aw_greater <= ( w_count + 1 > aw_count);
+ end
+ default: begin end
+ endcase
+ // }}}
+ //
+ // Read channel
+ // {{{
+
+ // r_count, r_zero, r_full
+ initial r_count = 0;
+ initial r_zero = 1;
+ initial r_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_count <= 0;
+ r_zero <= 1;
+ r_full <= 0;
+ end else case({(arskd_valid&&arskd_ready), S_AXI_RVALID&&S_AXI_RREADY})
+ 2'b10: begin
+ r_count <= r_count + 1;
+ r_zero <= 0;
+ r_full <= (r_count == { {(LGDEPTH-1){1'b1}}, 1'b0 });
+ end
+ 2'b01: begin
+ r_count <= r_count - 1;
+ r_zero <= (r_count <= 1);
+ r_full <= 0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Downstream write address channel
+ // {{{
+
+ initial downstream_aw_count = 0;
+ initial downstream_aw_zero = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ begin
+ downstream_aw_count <= 0;
+ downstream_aw_zero <= 1;
+ end else case({(M_AXI_AWVALID && M_AXI_AWREADY), M_AXI_BVALID && M_AXI_BREADY})
+ 2'b10: begin
+ downstream_aw_count <= downstream_aw_count + 1;
+ downstream_aw_zero <= 0;
+ end
+ 2'b01: begin
+ downstream_aw_count <= downstream_aw_count - 1;
+ downstream_aw_zero <= (downstream_aw_count <= 1);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Downstream write data channel
+ // {{{
+ initial downstream_w_count = 0;
+ initial downstream_w_zero = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ begin
+ downstream_w_count <= 0;
+ downstream_w_zero <= 1;
+ end else case({(M_AXI_WVALID && M_AXI_WREADY), M_AXI_BVALID && M_AXI_BREADY})
+ 2'b10: begin
+ downstream_w_count <= downstream_w_count + 1;
+ downstream_w_zero <= 0;
+ end
+ 2'b01: begin
+ downstream_w_count <= downstream_w_count - 1;
+ downstream_w_zero <= (downstream_w_count <= 1);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Downstream read channel
+ // {{{
+
+ initial downstream_r_count = 0;
+ initial downstream_r_zero = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ begin
+ downstream_r_count <= 0;
+ downstream_r_zero <= 1;
+ end else case({M_AXI_ARVALID && M_AXI_ARREADY, M_AXI_RVALID && M_AXI_RREADY})
+ 2'b10: begin
+ downstream_r_count <= downstream_r_count + 1;
+ downstream_r_zero <= 0;
+ end
+ 2'b01: begin
+ downstream_r_count <= downstream_r_count - 1;
+ downstream_r_zero <= (downstream_r_count <= 1);
+ end
+ default: begin end
+ endcase
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Timeout checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // The key piece here is that we define the timeout depending upon
+ // what happens (or doesn't happen) *DOWNSTREAM*. These timeouts
+ // will need to propagate upstream before taking place.
+
+ //
+ // Write address stall counter
+ // {{{
+ initial aw_stall_counter = 0;
+ initial aw_stall_limit = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault || !M_AXI_ARESETN)
+ begin
+ aw_stall_counter <= 0;
+ aw_stall_limit <= 0;
+ end else if (!M_AXI_AWVALID || M_AXI_AWREADY || M_AXI_BVALID)
+ begin
+ aw_stall_counter <= 0;
+ aw_stall_limit <= 0;
+ end else if (aw_w_greater && !M_AXI_WVALID)
+ begin
+ aw_stall_counter <= 0;
+ aw_stall_limit <= 0;
+ end else // if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ aw_stall_counter <= aw_stall_counter + 1;
+ aw_stall_limit <= (aw_stall_counter+1 >= OPT_TIMEOUT);
+ end
+ // }}}
+
+ //
+ // Write data stall counter
+ // {{{
+ initial w_stall_counter = 0;
+ initial w_stall_limit = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ begin
+ w_stall_counter <= 0;
+ w_stall_limit <= 0;
+ end else if (!M_AXI_WVALID || M_AXI_WREADY || M_AXI_BVALID)
+ begin
+ w_stall_counter <= 0;
+ w_stall_limit <= 0;
+ end else if (w_aw_greater && !M_AXI_AWVALID)
+ begin
+ w_stall_counter <= 0;
+ w_stall_limit <= 0;
+ end else // if (!M_AXI_BVALID || M_AXI_BREADY)
+ begin
+ w_stall_counter <= w_stall_counter + 1;
+ w_stall_limit <= (w_stall_counter + 1 >= OPT_TIMEOUT);
+ end
+ // }}}
+
+ //
+ // Write acknowledgment delay counter
+ // {{{
+ initial w_ack_timer = 0;
+ initial w_ack_limit = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ begin
+ w_ack_timer <= 0;
+ w_ack_limit <= 0;
+ end else if (M_AXI_BVALID || downstream_aw_zero || downstream_w_zero)
+ begin
+ w_ack_timer <= 0;
+ w_ack_limit <= 0;
+ end else
+ begin
+ w_ack_timer <= w_ack_timer + 1;
+ w_ack_limit <= (w_ack_timer + 1 >= OPT_TIMEOUT);
+ end
+ // }}}
+
+ //
+ // Read request stall counter
+ // {{{
+ initial r_stall_counter = 0;
+ initial r_stall_limit = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ begin
+ r_stall_counter <= 0;
+ r_stall_limit <= 0;
+ end else if (!M_AXI_ARVALID || M_AXI_ARREADY || M_AXI_RVALID)
+ begin
+ r_stall_counter <= 0;
+ r_stall_limit <= 0;
+ end else begin
+ r_stall_counter <= r_stall_counter + 1;
+ r_stall_limit <= (r_stall_counter + 1 >= OPT_TIMEOUT);
+ end
+ // }}}
+
+ //
+ // Read acknowledgement delay counter
+ // {{{
+ initial r_ack_timer = 0;
+ initial r_ack_limit = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ begin
+ r_ack_timer <= 0;
+ r_ack_limit <= 0;
+ end else if (M_AXI_RVALID || downstream_r_zero)
+ begin
+ r_ack_timer <= 0;
+ r_ack_limit <= 0;
+ end else begin
+ r_ack_timer <= r_ack_timer + 1;
+ r_ack_limit <= (r_ack_timer + 1 >= OPT_TIMEOUT);
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Fault detection
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Determine if a write fault has taken place
+ // {{{
+ initial o_write_fault =1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ o_write_fault <= 1'b0;
+ else if (OPT_SELF_RESET && o_write_fault)
+ begin
+ //
+ // Clear any fault on reset
+ if (!M_AXI_ARESETN)
+ o_write_fault <= 1'b0;
+ end else begin
+ //
+ // A write fault takes place if you respond without a prior
+ // bus request on both write address and write data channels.
+ if ((downstream_aw_zero || downstream_w_zero)&&(bskd_valid))
+ o_write_fault <= 1'b1;
+
+ // AXI-lite slaves are not allowed to return EXOKAY responses
+ // from the bus
+ if (bskd_valid && bskd_resp == EXOKAY)
+ o_write_fault <= 1'b1;
+
+ // If the downstream core refuses to accept either a
+ // write address request, or a write data request, or for that
+ // matter if it doesn't return an acknowledgment in a timely
+ // fashion, then a fault has been detected.
+ if (aw_stall_limit || w_stall_limit || w_ack_limit)
+ o_write_fault <= 1'b1;
+
+ // If the downstream core changes BRESP while VALID && !READY,
+ // then it isn't stalling the channel properly--that's a fault.
+ if (last_bchanged)
+ o_write_fault <= 1'b1;
+ end
+ // }}}
+
+ // o_read_fault
+ // {{{
+ initial o_read_fault =1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ o_read_fault <= 1'b0;
+ else if (OPT_SELF_RESET && o_read_fault)
+ begin
+ //
+ // Clear any fault on reset
+ if (!M_AXI_ARESETN)
+ o_read_fault <= 1'b0;
+ end else begin
+ // Responding without a prior request is a fault. Can only
+ // respond after a request has been made.
+ if (downstream_r_zero && rskd_valid)
+ o_read_fault <= 1'b1;
+
+ // AXI-lite slaves are not allowed to return EXOKAY. This is
+ // an error.
+ if (rskd_valid && rskd_resp == EXOKAY)
+ o_read_fault <= 1'b1;
+
+ // The slave cannot stall the bus forever, nor should the
+ // master wait forever for a response from the slave.
+ if (r_stall_limit || r_ack_limit)
+ o_read_fault <= 1'b1;
+
+ // If the slave changes the data, or the RRESP on the wire,
+ // while the incoming bus is stalled, then that's also a fault.
+ if (last_rchanged)
+ o_read_fault <= 1'b1;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Self reset handling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_SELF_RESET)
+ begin : SELF_RESET_GENERATION
+ // {{{
+ wire min_reset;
+
+ if (OPT_MIN_RESET > 1)
+ begin : MIN_RESET
+ // {{{
+ reg r_min_reset;
+ reg [$clog2(OPT_MIN_RESET+1):0] reset_counter;
+
+ //
+ // Optionally insist that any downstream reset have a
+ // minimum duration. Many Xilinx components require
+ // a 16-clock reset. This ensures such reset
+ // requirements are achieved.
+ //
+
+ initial reset_counter = OPT_MIN_RESET-1;
+ initial r_min_reset = 1'b0;
+ always @(posedge S_AXI_ARESETN)
+ if (M_AXI_ARESETN)
+ begin
+ reset_counter <= OPT_MIN_RESET-1;
+ r_min_reset <= 1'b0;
+ end else if (!M_AXI_ARESETN)
+ begin
+ if (reset_counter > 0)
+ reset_counter <= reset_counter-1;
+ min_reset <= (reset_counter <= 1);
+ end
+
+ assign min_reset = r_min_reset;
+`ifdef FORMAL
+ always @(*)
+ assert(reset_counter < OPT_MIN_RESET);
+ always @(*)
+ assert(min_reset == (reset_counter == 0));
+`endif
+ // }}}
+ end else begin : NO_MIN_RESET
+ // {{{
+ assign min_reset = 1'b1;
+ // }}}
+ end
+
+ // M_AXI_ARESETN
+ // {{{
+ // Reset the downstream bus on either a write or a read fault.
+ // Once the bus returns to idle, and any minimum reset durations
+ // have been achieved, then release the downstream from reset.
+ //
+ initial M_AXI_ARESETN = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_ARESETN <= 1'b0;
+ else if (o_write_fault || o_read_fault)
+ M_AXI_ARESETN <= 1'b0;
+ else if (aw_zero && w_zero && r_zero && min_reset
+ && !awskd_valid && !wskd_valid && !arskd_valid)
+ M_AXI_ARESETN <= 1'b1;
+ // }}}
+ // }}}
+ end else begin : SAME_RESET
+ // {{{
+ //
+ // The downstream reset equals the upstream reset
+ //
+ always @(*)
+ M_AXI_ARESETN = S_AXI_ARESETN;
+ // }}}
+ end endgenerate
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // {{{
+ //
+ // The following proof comes in several parts.
+ //
+ // 1. PROVE that the upstream properties will hold independent of
+ // what the downstream slave ever does.
+ //
+ // 2. PROVE that if the downstream slave follows protocol, then
+ // neither o_write_fault nor o_read_fault will never get raised.
+ //
+ // We then repeat these proofs again with both OPT_SELF_RESET set and
+ // clear. Which of the four proofs is accomplished is dependent upon
+ // parameters set by the formal engine.
+ //
+ //
+ wire [LGDEPTH:0] faxils_awr_outstanding, faxils_wr_outstanding,
+ faxils_rd_outstanding;
+
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Upstream master Bus properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ if (!f_past_valid)
+ begin
+ assume(!S_AXI_ARESETN);
+ assert(!M_AXI_ARESETN);
+ end
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_OPT_ASSUME_RESET(1'b1),
+ .F_OPT_NO_RESET((OPT_MIN_RESET == 0) ? 1:0),
+// .F_AXI_MAXWAIT((F_OPT_FAULTLESS) ? (2*OPT_TIMEOUT+2) : 0),
+ .F_AXI_MAXRSTALL(3),
+// .F_AXI_MAXDELAY(OPT_TIMEOUT+OPT_TIMEOUT+5),
+ .F_LGDEPTH(LGDEPTH+1),
+ //
+ .F_AXI_MAXWAIT((F_OPT_FAULTLESS) ? (2*OPT_TIMEOUT+2) : 0),
+ .F_AXI_MAXDELAY(2*OPT_TIMEOUT+3)
+ // }}}
+ ) axils (
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .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_awr_outstanding(faxils_awr_outstanding),
+ .f_axi_wr_outstanding(faxils_wr_outstanding),
+ .f_axi_rd_outstanding(faxils_rd_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ if (!F_OPT_WRITES)
+ begin
+ // {{{
+ assume(!S_AXI_AWVALID);
+ assume(!S_AXI_WVALID);
+ assert(aw_count == 0);
+ assert(w_count == 0);
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ // }}}
+ end
+
+ always @(*)
+ if (!F_OPT_READS)
+ begin
+ // {{{
+ assume(!S_AXI_ARVALID);
+ assert(r_count == 0);
+ assert(!S_AXI_RVALID);
+ assert(!M_AXI_ARVALID);
+ // }}}
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // General Induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+
+ always @(*)
+ begin
+ // {{{
+ assert(aw_zero == (aw_count == 0));
+ assert(w_zero == (w_count == 0));
+ assert(r_zero == (r_count == 0));
+
+ //
+ assert(aw_full == (&aw_count));
+ assert(w_full == (&w_count));
+ assert(r_full == (&r_count));
+ //
+ if (M_AXI_ARESETN && !o_write_fault)
+ begin
+ assert(downstream_aw_zero == (downstream_aw_count == 0));
+ assert(downstream_w_zero == (downstream_w_count == 0));
+ assert(downstream_aw_count + (M_AXI_AWVALID ? 1:0)
+ + (S_AXI_BVALID ? 1:0) == aw_count);
+ assert(downstream_w_count + (M_AXI_WVALID ? 1:0)
+ + (S_AXI_BVALID ? 1:0) == w_count);
+ end
+
+ if (M_AXI_ARESETN && !o_read_fault)
+ begin
+ assert(downstream_r_zero == (downstream_r_count == 0));
+ assert(downstream_r_count + (M_AXI_ARVALID ? 1:0)
+ + (S_AXI_RVALID ? 1:0) == r_count);
+ end
+ //
+ assert(aw_count == faxils_awr_outstanding);
+ assert(w_count == faxils_wr_outstanding);
+ assert(r_count == faxils_rd_outstanding);
+
+ assert(aw_w_greater == (aw_count > w_count));
+ assert(w_aw_greater == (w_count > aw_count));
+ // }}}
+ end
+ // }}}
+ generate if (F_OPT_FAULTLESS)
+ begin : ASSUME_FAULTLESS
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ // Assume the downstream core is protocol compliant, and
+ // prove that o_fault stays low.
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ wire [LGDEPTH:0] faxilm_awr_outstanding,
+ faxilm_wr_outstanding,
+ faxilm_rd_outstanding;
+
+ faxil_master #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_OPT_NO_RESET((OPT_MIN_RESET == 0) ? 1:0),
+ .F_AXI_MAXWAIT(OPT_TIMEOUT),
+ .F_AXI_MAXRSTALL(4),
+ .F_AXI_MAXDELAY(OPT_TIMEOUT),
+ .F_LGDEPTH(LGDEPTH+1)
+ // }}}
+ ) axilm (
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(M_AXI_ARESETN && S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awprot( M_AXI_AWPROT),
+ //
+ .i_axi_wvalid(M_AXI_WVALID),
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ //
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bresp( M_AXI_BRESP),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arprot( M_AXI_ARPROT),
+ //
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rresp( M_AXI_RRESP),
+ //
+ .f_axi_awr_outstanding(faxilm_awr_outstanding),
+ .f_axi_wr_outstanding(faxilm_wr_outstanding),
+ .f_axi_rd_outstanding(faxilm_rd_outstanding)
+ // }}}
+ );
+
+ //
+ // Here's the big proof
+ // {{{
+ always @(*)
+ assert(!o_write_fault);
+ always @(*)
+ assert(!o_read_fault);
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // The following properties are necessary for passing induction
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ begin
+ assert(!aw_stall_limit);
+ assert(!w_stall_limit);
+ assert(!w_ack_limit);
+
+ assert(!r_stall_limit);
+ assert(!r_ack_limit);
+
+ if (M_AXI_ARESETN)
+ begin
+ assert(downstream_aw_count == faxilm_awr_outstanding);
+ assert(downstream_w_count == faxilm_wr_outstanding);
+ assert(downstream_r_count == faxilm_rd_outstanding);
+ end
+ end
+
+ if (OPT_SELF_RESET)
+ begin
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid)
+ assert(M_AXI_ARESETN == $past(S_AXI_ARESETN));
+ end
+
+`ifdef VERIFIC
+ wire [LGDEPTH:0] f_axi_arstall;
+ wire [LGDEPTH:0] f_axi_awstall;
+ wire [LGDEPTH:0] f_axi_wstall;
+
+ assign f_axi_awstall = axilm.CHECK_STALL_COUNT.f_axi_awstall;
+ assign f_axi_wstall = axilm.CHECK_STALL_COUNT.f_axi_wstall;
+ assign f_axi_arstall = axilm.CHECK_STALL_COUNT.f_axi_arstall;
+
+ always @(*)
+ if (M_AXI_ARESETN && S_AXI_ARESETN && !o_write_fault)
+ assert(f_axi_awstall == aw_stall_counter);
+
+ always @(*)
+ if (M_AXI_ARESETN && S_AXI_ARESETN && !o_write_fault)
+ assert(f_axi_wstall == w_stall_counter);
+
+ always @(*)
+ if (M_AXI_ARESETN && S_AXI_ARESETN && !o_read_fault)
+ assert(f_axi_arstall == r_stall_counter);
+`endif
+ // }}}
+ // }}}
+ end else begin : WILD_DOWNSTREAM
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ // cover() checks, checks that only make sense if faults are
+ // possible
+ //
+
+ reg write_faulted, read_faulted, faulted;
+ reg [3:0] cvr_writes, cvr_reads;
+
+
+ if (OPT_SELF_RESET)
+ begin
+ ////////////////////////////////////////////////////////
+ //
+ // Prove that we can actually reset the downstream
+ // bus/core as desired
+ // {{{
+ ////////////////////////////////////////////////////////
+ //
+ //
+ initial write_faulted = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ write_faulted <= 0;
+ else if (o_write_fault)
+ write_faulted <= 1;
+
+
+ initial faulted = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ read_faulted <= 0;
+ else if (o_read_fault)
+ read_faulted <= 1;
+
+ always @(*)
+ faulted = (write_faulted || read_faulted);
+
+ always @(posedge S_AXI_ACLK)
+ cover(write_faulted && $rose(M_AXI_ARESETN));
+
+ always @(posedge S_AXI_ACLK)
+ cover(read_faulted && $rose(M_AXI_ARESETN));
+
+ always @(posedge S_AXI_ACLK)
+ cover(faulted && M_AXI_ARESETN && S_AXI_BVALID);
+
+ always @(posedge S_AXI_ACLK)
+ cover(faulted && M_AXI_ARESETN && S_AXI_RVALID);
+
+
+ initial cvr_writes = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault)
+ cvr_writes <= 0;
+ else if (write_faulted && S_AXI_BVALID && S_AXI_BREADY
+ && S_AXI_BRESP == OKAY
+ &&(!(&cvr_writes)))
+ cvr_writes <= cvr_writes + 1;
+
+ always @(*)
+ cover(cvr_writes > 5);
+
+ initial cvr_reads = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault)
+ cvr_reads <= 0;
+ else if (read_faulted && S_AXI_RVALID && S_AXI_RREADY
+ && S_AXI_RRESP == OKAY
+ &&(!(&cvr_reads)))
+ cvr_reads <= cvr_reads + 1;
+
+ always @(*)
+ cover(cvr_reads > 5);
+ // }}}
+ end
+
+ // }}}
+ end endgenerate
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Never data properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [C_AXI_DATA_WIDTH-1:0] fc_never_read_data;
+ (* anyconst *) reg [C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8-1:0]
+ fc_never_write_data;
+
+ (* anyconst *) reg [C_AXI_ADDR_WIDTH-1:0] fc_never_read_addr,
+ fc_never_write_addr;
+
+ // Write address checking
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && S_AXI_AWVALID)
+ assume(S_AXI_AWADDR != fc_never_write_addr);
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && !o_write_fault && M_AXI_AWVALID)
+ assert(M_AXI_AWADDR != fc_never_write_addr);
+ // }}}
+
+ // Write checking
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && S_AXI_WVALID)
+ assume({ S_AXI_WDATA, S_AXI_WSTRB } != fc_never_write_data);
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && !o_write_fault && M_AXI_WVALID)
+ assert({ M_AXI_WDATA, M_AXI_WSTRB } != fc_never_write_data);
+ // }}}
+
+ // Read address checking
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && S_AXI_ARVALID)
+ assume(S_AXI_ARADDR != fc_never_read_addr);
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && !o_read_fault && M_AXI_ARVALID)
+ assert(M_AXI_ARADDR != fc_never_read_addr);
+ // }}}
+
+ // Read checking
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && M_AXI_RVALID)
+ assume(M_AXI_RDATA != fc_never_read_data);
+
+ always @(*)
+ if (S_AXI_ARESETN && S_AXI_RVALID && S_AXI_RRESP == 2'b00)
+ assert(S_AXI_RDATA != fc_never_read_data);
+ // }}}
+
+ // }}}
+
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axilsingle.v b/rtl/wb2axip/axilsingle.v
new file mode 100644
index 0000000..e92db40
--- /dev/null
+++ b/rtl/wb2axip/axilsingle.v
@@ -0,0 +1,714 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilsingle.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Create a special AXI-lite slave which can be used to reduce
+// crossbar logic for multiple simplified AXI-lite slaves.
+//
+// To use this, the slave must follow specific (simplified AXI-lite) rules:
+//
+// Write interface
+// 1. The slave must guarantee that AWREADY == WREADY = 1
+// (This core doesn't have AWREADY or WREADY inputs)
+// 2. The slave must also guarantee that BVALID == $past(AWVALID)
+// (This core internally generates BVALID)
+// 3. The controller will guarantee that AWVALID == WVALID
+// (You can connect AWVALID to WVALID when connecting to your core)
+// 4. The controller will also guarantee that BREADY = 1
+// (This core doesn't have a BVALID input)
+//
+// Read interface
+// 1. The slave must guarantee that ARREADY == 1
+// (This core doesn't have an ARREADY input)
+// 2. The slave must also guarantee that RVALID == $past(ARVALID)
+// (This core doesn't have an RVALID input, trusting the slave
+// instead)
+// 3. The controller will guarantee that RREADY = 1
+// (This core doesn't have an RREADY output)
+//
+// In this simplified controller, the AxADDR lines have been dropped.
+// Slaves may only have one address, and that one will be aligned with the
+// bus width
+//
+//
+// Why? This simplifies slave logic. Slaves may interact with the bus
+// using only the logic below:
+//
+// always @(posedge S_AXI_ACLK)
+// if (AWVALID)
+// begin
+// if (WSTRB[0])
+// slvreg[ 7: 0] <= WDATA[ 7: 0];
+// if (WSTRB[1])
+// slvreg[15: 8] <= WDATA[15: 8];
+// if (WSTRB[2])
+// slvreg[23:16] <= WDATA[23:16];
+// if (WSTRB[3])
+// slvreg[31:24] <= WDATA[31:24];
+// end
+//
+// always @(*)
+// BRESP = 2'b00; // Or other constant, such as 2'b10
+//
+// always @(posedge S_AXI_ACLK)
+// if (ARVALID)
+// RDATA <= slvreg;
+//
+// always @(*)
+// RRESP = 2'b00; // Or other constant, such as 2'b10
+//
+// This core will then keep track of the more complex bus logic,
+// simplifying both slaves and connection logic. Slaves with the more
+// complicated (and proper/accurate) logic, but with only one bus address,
+// and that follow the rules above, should have no problems with this
+// additional logic.
+//
+// Performance:
+//
+// This core can sustain one read/write per clock as long as the upstream
+// AXI-Lite master keeps S_AXI_[BR]READY high. If S_AXI_[BR]READY ever
+// drops, there's some flexibility provided by the return FIFO, so the
+// master might not notice a drop in throughput until the FIFO fills.
+//
+// The more practical performance measure is the latency of this core.
+// That is measured at four clocks contingent (again) on the master holding
+// S_AXI_[BR]READY line high.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// `ifdef VERILATOR
+// `define FORMAL
+// `endif
+// }}}
+module axilsingle #(
+ // {{{
+ // NS is the number of slave interfaces
+ parameter NS = 16,
+ //
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ localparam integer C_AXI_ADDR_WIDTH = $clog2(NS)+$clog2(C_AXI_DATA_WIDTH)-3,
+ //
+ // LGFLEN specifies the log (based two) of the number of
+ // transactions that may need to be held outstanding internally.
+ // If you really want high throughput, and if you expect any
+ // back pressure at all, then increase LGFLEN. Otherwise the
+ // default value of 3 (FIFO size = 8) should be sufficient
+ // to maintain full loading
+ parameter LGFLEN=3,
+ //
+ // If set, OPT_LOWPOWER will set all unused registers, both
+ // internal and external, to zero anytime their corresponding
+ // *VALID bit is clear
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [3-1:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire [2-1:0] S_AXI_BRESP,
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ //
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [3-1:0] S_AXI_ARPROT,
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ //
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [2-1:0] S_AXI_RRESP,
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ //
+ //
+ //
+ output wire [NS-1:0] M_AXI_AWVALID,
+ output wire [3-1:0] M_AXI_AWPROT,
+ //
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ //
+ input wire [NS*2-1:0] M_AXI_BRESP,
+ //
+ output wire [NS-1:0] M_AXI_ARVALID,
+ output wire [3-1:0] M_AXI_ARPROT,
+ //
+ input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [NS*2-1:0] M_AXI_RRESP
+ // }}}
+ );
+
+ //
+ // AW, and DW, are short-hand abbreviations used locally.
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam LGNS = $clog2(NS);
+ //
+ localparam INTERCONNECT_ERROR = 2'b11;
+ localparam ADDR_LSBS = $clog2(DW)-3;
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write logic:
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire awskid_valid, bffull, bempty, write_awskidready;
+ reg write_bvalid, write_response;
+ reg bfull, write_wready;
+ wire [AW-ADDR_LSBS-1:0] awskid_addr;
+ reg [AW-ADDR_LSBS-1:0] write_windex, write_bindex;
+ wire [3-1:0] awskid_prot;
+ reg [3-1:0] m_axi_awprot;
+ wire [LGFLEN:0] bfill;
+ reg [LGFLEN:0] write_count;
+ reg [1:0] write_resp;
+ reg [NS-1:0] m_axi_awvalid;
+
+ skidbuffer #(.OPT_LOWPOWER(OPT_LOWPOWER), .OPT_OUTREG(0),
+ .DW((AW-ADDR_LSBS)+3))
+ awskid( .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID),
+ .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWPROT, S_AXI_AWADDR[AW-1:ADDR_LSBS] }),
+ .o_valid(awskid_valid), .i_ready(write_awskidready),
+ .o_data({ awskid_prot, awskid_addr }));
+
+ initial write_wready = 0;
+ initial m_axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { write_wready, m_axi_awvalid } <= 0;
+ else if (awskid_valid && write_awskidready)
+ begin
+ m_axi_awvalid <= 0;
+ m_axi_awvalid <= (1<<awskid_addr);
+ // if (awskid_addr >= NS)
+ // m_axi_awvalid[NS] <= 1'b1;
+
+ write_wready <= 1;
+ end else if (S_AXI_WVALID)
+ { write_wready, m_axi_awvalid } <= 0;
+
+ assign S_AXI_WREADY = write_wready;
+
+ always @(posedge S_AXI_ACLK)
+ if (awskid_valid && write_awskidready)
+ begin
+ m_axi_awprot <= awskid_prot;
+ write_windex <= awskid_addr;
+ end
+
+ assign M_AXI_AWVALID = (S_AXI_WVALID) ? m_axi_awvalid : {(NS){1'b0}};
+ assign M_AXI_AWPROT = m_axi_awprot;
+ assign S_AXI_WREADY = write_wready;
+ assign M_AXI_WDATA = S_AXI_WDATA;
+ assign M_AXI_WSTRB = S_AXI_WSTRB;
+
+ assign write_awskidready = (!write_wready || S_AXI_WVALID) && !bfull;
+
+ initial { write_response, write_bvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { write_response, write_bvalid } <= 0;
+ else
+ { write_response, write_bvalid }
+ <= { write_bvalid, (S_AXI_WVALID && S_AXI_WREADY) };
+
+ always @(posedge S_AXI_ACLK)
+ write_bindex <= write_windex;
+
+ generate if (LGNS == $clog2(NS+1))
+ begin : GEN_WRITE_ERR
+ always @(posedge S_AXI_ACLK)
+ if (write_bindex >= NS)
+ write_resp <= INTERCONNECT_ERROR;
+ else
+ write_resp <= M_AXI_BRESP[2*write_bindex +: 2];
+ end else begin : NO_WRITE_ERR
+ always @(posedge S_AXI_ACLK)
+ write_resp <= M_AXI_BRESP[2*write_bindex +: 2];
+ end endgenerate
+
+ initial write_count = 0;
+ initial bfull = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ write_count <= 0;
+ bfull <= 0;
+ end else case({ (awskid_valid && write_awskidready),
+ (S_AXI_BVALID & S_AXI_BREADY) })
+ 2'b01: begin
+ write_count <= write_count - 1;
+ bfull <= 1'b0;
+ end
+ 2'b10: begin
+ write_count <= write_count + 1;
+ bfull <= (&write_count[LGFLEN-1:0]);
+ end
+ default: begin end
+ endcase
+
+`ifdef FORMAL
+ always @(*)
+ assert(write_count <= { 1'b1, {(LGFLEN){1'b0}} });
+ always @(*)
+ assert(bfull == (write_count == { 1'b1, {(LGFLEN){1'b0}} }));
+`endif
+
+ sfifo #(.BW(2), .OPT_ASYNC_READ(0), .LGFLEN(LGFLEN))
+ bfifo ( .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(write_response), .i_data(write_resp), .o_full(bffull),
+ .o_fill(bfill),
+ .i_rd(S_AXI_BVALID && S_AXI_BREADY), .o_data(S_AXI_BRESP),
+ .o_empty(bempty));
+
+`ifdef FORMAL
+ always @(*)
+ assert(write_count == bfill
+ + (write_response ? 1:0)
+ + (write_bvalid ? 1:0)
+ + (write_wready ? 1:0));
+
+`ifdef VERIFIC
+ always @(*)
+ if (bfifo.f_first_in_fifo)
+ assert(bfifo.f_first_data != 2'b01);
+ always @(*)
+ if (bfifo.f_second_in_fifo)
+ assert(bfifo.f_second_data != 2'b01);
+ always @(*)
+ if (!bempty && (bfifo.rd_addr != bfifo.f_first_addr)
+ &&(bfifo.rd_addr != bfifo.f_second_addr))
+ assume(S_AXI_BRESP != 2'b01);
+`else
+ always @(*)
+ if (!bempty)
+ assume(S_AXI_BRESP != 2'b01);
+`endif
+`endif
+
+ assign S_AXI_BVALID = !bempty;
+
+`ifdef FORMAL
+ always @(*)
+ assert(!bffull || !write_bvalid);
+`endif
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read logic
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ wire rempty, rdfull;
+ wire [LGFLEN:0] rfill;
+ reg [AW-ADDR_LSBS-1:0] read_index, last_read_index;
+ reg [1:0] read_resp;
+ reg [DW-1:0] read_rdata;
+ reg read_rwait, read_rvalid, read_result;
+ reg [NS-1:0] m_axi_arvalid;
+ reg [3-1:0] m_axi_arprot;
+
+ initial { m_axi_arvalid, read_rwait } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { m_axi_arvalid, read_rwait } <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ m_axi_arvalid <= (1 << S_AXI_ARADDR[AW-1:ADDR_LSBS]);
+ read_rwait <= 1'b1;
+ end else
+ { m_axi_arvalid, read_rwait } <= 0;
+
+ always @(posedge S_AXI_ACLK)
+ begin
+ m_axi_arprot <= S_AXI_ARPROT;
+ read_index <= S_AXI_ARADDR[AW-1:ADDR_LSBS];
+ end
+
+ assign M_AXI_ARVALID = m_axi_arvalid;
+ assign M_AXI_ARPROT = m_axi_arprot;
+
+ initial { read_result, read_rvalid } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_result, read_rvalid } <= 2'b00;
+ else
+ { read_result, read_rvalid } <= { read_rvalid, read_rwait };
+
+ always @(posedge S_AXI_ACLK)
+ last_read_index <= read_index;
+
+ always @(posedge S_AXI_ACLK)
+ read_rdata <= M_AXI_RDATA[DW*last_read_index +: DW];
+
+ generate if (LGNS == $clog2(NS+1))
+ begin : GEN_READ_ERR
+ always @(posedge S_AXI_ACLK)
+ if (last_read_index >= NS)
+ read_resp <= INTERCONNECT_ERROR;
+ else
+ read_resp <= M_AXI_RRESP[2*last_read_index +: 2];
+ end else begin : NO_READ_ERR
+ always @(posedge S_AXI_ACLK)
+ read_resp <= M_AXI_RRESP[2*last_read_index +: 2];
+ end endgenerate
+
+ reg read_full;
+ reg [LGFLEN:0] read_count;
+
+ initial { read_count, read_full } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ { read_count, read_full } <= 0;
+ else case({ S_AXI_ARVALID & S_AXI_ARREADY, S_AXI_RVALID & S_AXI_RREADY})
+ 2'b10: begin
+ read_count <= read_count + 1;
+ read_full <= &read_count[LGFLEN-1:0];
+ end
+ 2'b01: begin
+ read_count <= read_count - 1;
+ read_full <= 1'b0;
+ end
+ default: begin end
+ endcase
+
+`ifdef FORMAL
+ always @(*)
+ assert(read_count <= { 1'b1, {(LGFLEN){1'b0}} });
+ always @(*)
+ assert(read_full == (read_count == { 1'b1, {(LGFLEN){1'b0}} }));
+`endif
+ assign S_AXI_ARREADY = !read_full;
+
+ sfifo #(.BW(DW+2), .OPT_ASYNC_READ(0), .LGFLEN(LGFLEN))
+ rfifo ( .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(read_result), .i_data({ read_rdata, read_resp }),
+ .o_full(rdfull), .o_fill(rfill),
+ .i_rd(S_AXI_RVALID && S_AXI_RREADY),
+ .o_data({ S_AXI_RDATA, S_AXI_RRESP }),.o_empty(rempty));
+
+`ifdef FORMAL
+ always @(*)
+ assert(read_count == rfill + read_result + read_rvalid + read_rwait);
+`ifdef VERIFIC
+ always @(*)
+ if (rfifo.f_first_in_fifo)
+ assert(rfifo.f_first_data[1:0] != 2'b01);
+ always @(*)
+ if (rfifo.f_second_in_fifo)
+ assert(rfifo.f_second_data[1:0] != 2'b01);
+ always @(*)
+ if (!rempty && (rfifo.rd_addr != rfifo.f_first_addr)
+ &&(rfifo.rd_addr != rfifo.f_second_addr))
+ assume(S_AXI_RRESP != 2'b01);
+`else
+ always @(*)
+ if (!rempty)
+ assume(S_AXI_RRESP != 2'b01);
+`endif
+`endif
+
+ assign S_AXI_RVALID = !rempty;
+
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+ S_AXI_AWADDR[ADDR_LSBS-1:0],
+ S_AXI_ARADDR[ADDR_LSBS-1:0],
+ bffull, rdfull, bfill, rfill };
+ // verilator lint_on UNUSED
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGFLEN+1;
+ reg f_past_valid;
+ reg [F_LGDEPTH-1:0] count_awr_outstanding, count_wr_outstanding,
+ count_rd_outstanding;
+
+
+ wire [(F_LGDEPTH-1):0] f_axi_awr_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_rd_outstanding;
+
+ wire [1:0] fm_axi_awr_outstanding [0:NS-1];
+ wire [1:0] fm_axi_wr_outstanding [0:NS-1];
+ wire [1:0] fm_axi_rd_outstanding [0:NS-1];
+
+ reg [NS-1:0] m_axi_rvalid, m_axi_bvalid;
+
+ faxil_slave #(// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ // .F_OPT_NO_READS(1'b0),
+ // .F_OPT_NO_WRITES(1'b0),
+ .F_OPT_XILINX(1),
+ .F_LGDEPTH(F_LGDEPTH))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awprot(S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp(S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arprot(S_AXI_ARPROT),
+ //
+ .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(f_axi_rd_outstanding),
+ .f_axi_wr_outstanding(f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding));
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ genvar M;
+ generate for(M=0; M<NS; M=M+1)
+ begin : CONSTRAIN_SLAVE_INTERACTIONS
+
+ faxil_master #(// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(1),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ // .F_OPT_NO_READS(1'b0),
+ // .F_OPT_NO_WRITES(1'b0),
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(2))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID[M]),
+ .i_axi_awready(1'b1),
+ .i_axi_awaddr(1'b0),
+ .i_axi_awprot(M_AXI_AWPROT),
+ //
+ .i_axi_wvalid(M_AXI_AWVALID[M]),
+ .i_axi_wready(1'b1),
+ .i_axi_wdata(M_AXI_WDATA[C_AXI_DATA_WIDTH-1:0]),
+ .i_axi_wstrb(M_AXI_WSTRB[C_AXI_DATA_WIDTH/8-1:0]),
+ //
+ .i_axi_bvalid(m_axi_bvalid[M]),
+ .i_axi_bready(1'b1),
+ .i_axi_bresp(M_AXI_BRESP[2*M +: 2]),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID[M]),
+ .i_axi_arready(1'b1),
+ .i_axi_araddr(1'b0),
+ .i_axi_arprot(M_AXI_ARPROT),
+ //
+ .i_axi_rvalid(m_axi_rvalid[M]),
+ .i_axi_rready(1'b1),
+ .i_axi_rdata(M_AXI_RDATA[M*C_AXI_DATA_WIDTH +: C_AXI_DATA_WIDTH]),
+ .i_axi_rresp(M_AXI_RRESP[2*M +: 2]),
+ //
+ .f_axi_rd_outstanding(fm_axi_rd_outstanding[M]),
+ .f_axi_wr_outstanding(fm_axi_wr_outstanding[M]),
+ .f_axi_awr_outstanding(fm_axi_awr_outstanding[M]));
+
+ initial m_axi_bvalid <= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_bvalid[M] <= 1'b0;
+ else
+ m_axi_bvalid[M] <= M_AXI_AWVALID[M];
+
+ initial m_axi_rvalid[M] <= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_axi_rvalid[M] <= 1'b0;
+ else
+ m_axi_rvalid[M] <= M_AXI_ARVALID[M];
+
+ always @(*)
+ assert(fm_axi_awr_outstanding[M] == fm_axi_wr_outstanding[M]);
+
+ always @(*)
+ assert(fm_axi_wr_outstanding[M]== (m_axi_bvalid[M] ? 1:0));
+
+ always @(*)
+ assert(fm_axi_rd_outstanding[M]== (m_axi_rvalid[M] ? 1:0));
+ end endgenerate
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Properties necessary to pass induction
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ assert(S_AXI_WREADY == (m_axi_awvalid != 0));
+`ifdef VERIFIC
+ always @(*)
+ assert($onehot0(M_AXI_AWVALID));
+
+ always @(*)
+ assert($onehot0(m_axi_bvalid));
+
+ always @(*)
+ assert($onehot0(m_axi_rvalid));
+`endif
+ always @(*)
+ begin
+ count_awr_outstanding = 0;
+ if (!S_AXI_AWREADY)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_wready)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_bvalid)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ if (write_response)
+ count_awr_outstanding = count_awr_outstanding + 1;
+ count_awr_outstanding = count_awr_outstanding + bfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_awr_outstanding == count_awr_outstanding);
+
+ always @(*)
+ begin
+ count_wr_outstanding = 0;
+ if (write_bvalid)
+ count_wr_outstanding = count_wr_outstanding + 1;
+ if (write_response)
+ count_wr_outstanding = count_wr_outstanding + 1;
+ count_wr_outstanding = count_wr_outstanding + bfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_wr_outstanding == count_wr_outstanding);
+
+ //
+ //
+ //
+ always @(*)
+ begin
+ count_rd_outstanding = 0;
+ if (read_rwait)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ if (read_rvalid)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ if (read_result)
+ count_rd_outstanding = count_rd_outstanding + 1;
+ count_rd_outstanding = count_rd_outstanding + rfill;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(f_axi_rd_outstanding == count_rd_outstanding);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [3:0] cvr_arvalids, cvr_awvalids, cvr_reads, cvr_writes;
+
+ initial cvr_awvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_awvalids <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && !(&cvr_awvalids))
+ cvr_awvalids <= cvr_awvalids + 1;
+
+ initial cvr_arvalids = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_arvalids <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && !(&cvr_arvalids))
+ cvr_arvalids <= cvr_arvalids + 1;
+
+ initial cvr_writes = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_writes <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY && !(&cvr_writes))
+ cvr_writes <= cvr_writes + 1;
+
+ initial cvr_reads = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_reads <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && !(&cvr_arvalids))
+ cvr_reads <= cvr_reads + 1;
+
+ always @(*)
+ cover(cvr_awvalids > 4);
+
+ always @(*)
+ cover(cvr_arvalids > 4);
+
+ always @(*)
+ cover(cvr_reads > 4);
+
+ always @(*)
+ cover(cvr_writes > 4);
+
+ always @(*)
+ cover((cvr_writes > 4) && (cvr_reads > 4));
+`endif
+endmodule
+// `ifndef YOSYS
+// `default_nettype wire
+// `endif
diff --git a/rtl/wb2axip/axilupsz.v b/rtl/wb2axip/axilupsz.v
new file mode 100644
index 0000000..29dc41d
--- /dev/null
+++ b/rtl/wb2axip/axilupsz.v
@@ -0,0 +1,603 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilupsz.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Converts AXI4-lite traffic of one data width to a similar
+// AXI4-lite interface with a larger data path.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2021-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilupsz #(
+ // {{{
+ parameter C_S_AXIL_DATA_WIDTH = 32,
+ parameter C_M_AXIL_DATA_WIDTH = 64,
+ parameter C_AXIL_ADDR_WIDTH = 32,
+ parameter LGFIFO = 5,
+ parameter [0:0] OPT_LOWPOWER = 1,
+ localparam SDW = C_S_AXIL_DATA_WIDTH,
+ localparam MDW = C_M_AXIL_DATA_WIDTH,
+ localparam AW = C_AXIL_ADDR_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ S_AXI_ARESETN,
+ // The slave interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [AW-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [SDW-1:0] S_AXIL_WDATA,
+ input wire [SDW/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [AW-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [SDW-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ // The master interface
+ // {{{
+ output wire M_AXIL_AWVALID,
+ input wire M_AXIL_AWREADY,
+ output wire [AW-1:0] M_AXIL_AWADDR,
+ output wire [2:0] M_AXIL_AWPROT,
+ //
+ output wire M_AXIL_WVALID,
+ input wire M_AXIL_WREADY,
+ output wire [MDW-1:0] M_AXIL_WDATA,
+ output wire [MDW/8-1:0] M_AXIL_WSTRB,
+ //
+ input wire M_AXIL_BVALID,
+ output wire M_AXIL_BREADY,
+ input wire [1:0] M_AXIL_BRESP,
+ //
+ output wire M_AXIL_ARVALID,
+ input wire M_AXIL_ARREADY,
+ output wire [AW-1:0] M_AXIL_ARADDR,
+ output wire [2:0] M_AXIL_ARPROT,
+ //
+ input wire M_AXIL_RVALID,
+ output wire M_AXIL_RREADY,
+ input wire [MDW-1:0] M_AXIL_RDATA,
+ input wire [1:0] M_AXIL_RRESP
+ // }}}
+ // }}}
+ );
+
+ localparam MLSB = $clog2(C_M_AXIL_DATA_WIDTH/8);
+ localparam SLSB = $clog2(C_S_AXIL_DATA_WIDTH/8);
+ localparam RPTS = MDW/SDW;
+
+ generate if (SDW == MDW)
+ begin : NO_CHANGE
+ // {{{
+ assign M_AXIL_AWVALID = S_AXIL_AWVALID;
+ assign S_AXIL_AWREADY = M_AXIL_AWREADY;
+ assign M_AXIL_AWADDR = S_AXIL_AWADDR;
+ assign M_AXIL_AWPROT = S_AXIL_AWPROT;
+
+ assign M_AXIL_WVALID = S_AXIL_WVALID;
+ assign S_AXIL_WREADY = M_AXIL_WREADY;
+ assign M_AXIL_WDATA = S_AXIL_WDATA;
+ assign M_AXIL_WSTRB = S_AXIL_WSTRB;
+
+ assign S_AXIL_BVALID = M_AXIL_BVALID;
+ assign M_AXIL_BREADY = S_AXIL_BREADY;
+ assign S_AXIL_BRESP = M_AXIL_BRESP;
+
+ assign M_AXIL_ARVALID = S_AXIL_ARVALID;
+ assign S_AXIL_ARREADY = M_AXIL_ARREADY;
+ assign M_AXIL_ARADDR = S_AXIL_ARADDR;
+ assign M_AXIL_ARPROT = S_AXIL_ARPROT;
+
+ assign S_AXIL_RVALID = M_AXIL_RVALID;
+ assign M_AXIL_RREADY = S_AXIL_RREADY;
+ assign S_AXIL_RDATA = M_AXIL_RDATA;
+ assign S_AXIL_RRESP = M_AXIL_RRESP;
+ // }}}
+ end else begin : UPSIZE_BUS_DATA_WIDTH
+ // {{{
+ // Signal declarations
+ // {{{
+ wire awskd_valid, wskd_valid, wskd_ready;
+ wire [AW-1:0] awskd_addr;
+ wire [2:0] awskd_prot;
+
+ wire [SDW-1:0] wskd_data;
+ wire [SDW/8-1:0] wskd_strb;
+
+ reg awvalid, wvalid;
+ reg [AW-1:0] awaddr;
+ reg [2:0] awprot;
+ reg [MDW-1:0] wdata;
+ reg [MDW/8-1:0] wstrb;
+
+ reg rvalid;
+ reg [SDW-1:0] rdata;
+ reg [1:0] rresp;
+ wire rfifo_full, rfifo_empty;
+ wire [LGFIFO:0] rfifo_fill;
+ wire [MLSB-SLSB-1:0] rfifo_data;
+ wire [MDW-1:0] shift_rdata;
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Write channel(s)
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // AW* skid bufer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .DW(AW+3)
+ // }}}
+ ) awskd (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data({ S_AXIL_AWADDR, S_AXIL_AWPROT }),
+ .o_valid(awskd_valid), .i_ready(wskd_ready),
+ .o_data({ awskd_addr, awskd_prot })
+ // }}}
+ );
+ // }}}
+
+ skidbuffer #(
+ // {{{
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0),
+ .DW(SDW+SDW/8)
+ // }}}
+ ) wskd (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(wskd_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ // wskd_ready
+ // {{{
+ // We *need* to synchronize the two channels here. We need the
+ // address informatiuon to know how to set the W* channel
+ // downstream
+ assign wskd_ready = (awskd_valid && wskd_valid)
+ && (!M_AXIL_AWVALID || M_AXIL_AWREADY)
+ && (!M_AXIL_WVALID || M_AXIL_WREADY);
+ // }}}
+
+ // awvalid
+ // {{{
+ initial awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ awvalid <= 0;
+ else if (!M_AXIL_AWVALID || M_AXIL_AWREADY)
+ awvalid <= wskd_ready;
+ // }}}
+
+ // awaddr, awprot
+ // {{{
+ initial { awaddr, awprot } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ { awaddr, awprot } <= 0;
+ else if (!M_AXIL_AWVALID || M_AXIL_AWREADY)
+ begin
+ { awaddr, awprot } <= 0;
+ if (awskd_valid || !OPT_LOWPOWER)
+ {awaddr, awprot } <= { awskd_addr, awskd_prot };
+ end
+ // }}}
+
+ // wvalid
+ // {{{
+ initial wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ wvalid <= 0;
+ else if (!M_AXIL_WVALID || M_AXIL_WREADY)
+ wvalid <= wskd_ready;
+ // }}}
+
+ // wdata, wstrb
+ // {{{
+ initial { wdata, wstrb } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ { wdata, wstrb } <= 0;
+ else if (!M_AXIL_WVALID || M_AXIL_WREADY)
+ begin
+ // Default values
+ wstrb <= 0;
+ wdata <= {(RPTS){ wskd_data }};
+
+ // Verilator lint_off WIDTH
+ if (OPT_LOWPOWER)
+ begin
+ wdata <= 0;
+ wdata <= (wskd_data)
+ << (awskd_addr[MLSB-1:SLSB] * SDW);
+ end
+
+ if (!OPT_LOWPOWER || wskd_valid)
+ wstrb <= (wskd_strb)
+ << (awskd_addr[MLSB-1:SLSB] * SDW);
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Read channel
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ wire rskd_valid, rskd_ready;
+ wire [MDW-1:0] rskd_data;
+ wire [1:0] rskd_resp;
+
+ // Read LSB address FIFO
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(MLSB-SLSB),
+ .LGFLEN(LGFIFO)
+ // }}}
+ ) rfifo (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_wr(S_AXIL_ARVALID && S_AXIL_ARREADY),
+ .i_data(M_AXIL_ARADDR[MLSB-1:SLSB]),
+ .o_full(rfifo_full), .o_fill(rfifo_fill),
+ .i_rd(M_AXIL_RVALID && M_AXIL_RREADY),
+ .o_data(rfifo_data),
+ .o_empty(rfifo_empty)
+ // }}}
+ );
+ // }}}
+
+ // Read return skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(0),
+ .DW(MDW+2)
+ // }}}
+ ) rskd (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(M_AXIL_RVALID), .o_ready(M_AXIL_RREADY),
+ .i_data({ M_AXIL_RDATA, M_AXIL_RRESP }),
+ .o_valid(rskd_valid), .i_ready(rskd_ready),
+ .o_data({ rskd_data, rskd_resp })
+ // }}}
+ );
+
+ assign rskd_ready = !S_AXIL_RVALID || S_AXIL_RREADY;
+ // }}}
+
+ assign shift_rdata = rskd_data
+ >> ({ {(MDW-(MLSB-SLSB)){1'b0}}, rfifo_data} * SDW);
+
+
+ // rvalid
+ // {{{
+ initial rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rvalid <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ rvalid <= rskd_valid;
+ // }}}
+
+ // rresp
+ // {{{
+ initial rresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ rresp <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ begin
+ rresp <= rskd_resp;
+ if (OPT_LOWPOWER && !rskd_valid)
+ rresp <= 0;
+ end
+ // }}}
+
+ // rdata
+ // {{{
+ initial rdata = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ rdata <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ begin
+ rdata <= shift_rdata[SDW-1:0];
+
+ if (OPT_LOWPOWER && !rskd_valid)
+ rdata <= 0;
+ end
+`ifdef FORMAL
+ // Low power check
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && OPT_LOWPOWER && !S_AXIL_RVALID)
+ begin
+ assert(rdata == 0);
+ assert(rresp == 0);
+ end
+ // }}}
+`endif
+ // }}}
+
+ // }}}
+
+ // M_* values, S_B*
+ // {{{
+ assign M_AXIL_AWVALID = awvalid;
+ assign M_AXIL_AWADDR = awaddr;
+ assign M_AXIL_AWPROT = awprot;
+ assign M_AXIL_WVALID = wvalid;
+ assign M_AXIL_WDATA = wdata;
+ assign M_AXIL_WSTRB = wstrb;
+
+ assign M_AXIL_ARVALID = S_AXIL_ARVALID && !rfifo_full;
+ assign S_AXIL_ARREADY = M_AXIL_ARREADY && !rfifo_full;
+ assign M_AXIL_ARADDR = S_AXIL_ARADDR;
+ assign M_AXIL_ARPROT = S_AXIL_ARPROT;
+
+ assign S_AXIL_RVALID = rvalid;
+ assign S_AXIL_RDATA = rdata;
+ assign S_AXIL_RRESP = rresp;
+
+ assign S_AXIL_BVALID = M_AXIL_BVALID;
+ assign M_AXIL_BREADY = S_AXIL_BREADY;
+ assign S_AXIL_BRESP = M_AXIL_BRESP;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, shift_rdata[MDW-1:SDW],
+ rfifo_empty, rfifo_fill };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ // Formal properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGFIFO+2;
+ wire [F_LGDEPTH-1:0] fslv_rd_outstanding,
+ fmst_rd_outstanding,
+ fslv_wr_outstanding,
+ fmst_wr_outstanding,
+ fslv_awr_outstanding,
+ fmst_awr_outstanding;
+ ////////////////////////////////////////////////////////////////
+ //
+ // Interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(SDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_COVER_BURST(4),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_AXI_MAXWAIT(8),
+ .F_AXI_MAXRSTALL(3),
+ .F_AXI_MAXDELAY(16)
+ // }}}
+ ) axil_slave (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(fslv_rd_outstanding),
+ .f_axi_wr_outstanding(fslv_wr_outstanding),
+ .f_axi_awr_outstanding(fslv_awr_outstanding)
+ // }}}
+ );
+
+ faxil_master #(
+ // {{{
+ .C_AXI_DATA_WIDTH(MDW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_COVER_BURST(1),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_OPT_NO_RESET(1),
+ .F_AXI_MAXWAIT(5),
+ .F_AXI_MAXRSTALL(3),
+ .F_AXI_MAXDELAY(5)
+ // }}}
+ ) axil_master (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXIL_AWVALID),
+ .i_axi_awready(M_AXIL_AWREADY),
+ .i_axi_awaddr( M_AXIL_AWADDR),
+ .i_axi_awprot( M_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(M_AXIL_WVALID),
+ .i_axi_wready(M_AXIL_WREADY),
+ .i_axi_wdata( M_AXIL_WDATA),
+ .i_axi_wstrb( M_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(M_AXIL_BVALID),
+ .i_axi_bready(M_AXIL_BREADY),
+ .i_axi_bresp( M_AXIL_BRESP),
+ //
+ .i_axi_arvalid(M_AXIL_ARVALID),
+ .i_axi_arready(M_AXIL_ARREADY),
+ .i_axi_araddr( M_AXIL_ARADDR),
+ .i_axi_arprot( M_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(M_AXIL_RVALID),
+ .i_axi_rready(M_AXIL_RREADY),
+ .i_axi_rdata( M_AXIL_RDATA),
+ .i_axi_rresp( M_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(fmst_rd_outstanding),
+ .f_axi_wr_outstanding(fmst_wr_outstanding),
+ .f_axi_awr_outstanding(fmst_awr_outstanding)
+ // }}}
+ );
+
+ // Correlate slave and master write outstanding counters
+ // {{{
+ always @(*)
+ begin
+ assume(fslv_awr_outstanding <= (1<<LGFIFO));
+ assume(fslv_wr_outstanding <= (1<<LGFIFO));
+
+ assert(fslv_awr_outstanding + (S_AXIL_WREADY ? 0:1)
+ == fslv_wr_outstanding +(S_AXIL_AWREADY ? 0:1));
+
+ assert(fmst_awr_outstanding + (M_AXIL_AWVALID ? 1:0)
+ == fmst_wr_outstanding + (M_AXIL_WVALID ? 1:0));
+
+ assert(fslv_awr_outstanding == fmst_awr_outstanding
+ +(M_AXIL_AWVALID ? 1:0)+(S_AXIL_AWREADY ? 0:1));
+ assert(fslv_wr_outstanding == fmst_wr_outstanding
+ +(M_AXIL_WVALID ? 1:0) + (S_AXIL_WREADY ? 0:1));
+ end
+ // }}}
+
+ // Correlate the slave and master read outstanding counters
+ // w/ FIFO fill
+ // {{{
+ always @(*)
+ begin
+ assert(fslv_rd_outstanding == fmst_rd_outstanding
+ +(S_AXIL_RVALID ? 1:0) + (M_AXIL_RREADY ? 0:1));
+ assert(rfifo_fill == fmst_rd_outstanding);
+
+ if (M_AXIL_RVALID)
+ assert(!rfifo_empty);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Not implemented yet
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // (Currently) captured by the interface properties
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // None
+
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+ // Parameter checking
+ // {{{
+ initial if (SDW > MDW) $stop;
+ // }}}
+endmodule
diff --git a/rtl/wb2axip/axilwr2wbsp.v b/rtl/wb2axip/axilwr2wbsp.v
new file mode 100644
index 0000000..9b75c0e
--- /dev/null
+++ b/rtl/wb2axip/axilwr2wbsp.v
@@ -0,0 +1,675 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilwr2wbsp.v (AXI lite to wishbone slave, read channel)
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Bridge an AXI lite write channel triplet to a single wishbone
+// write channel. A full AXI lite to wishbone bridge will also
+// require the read channel and an arbiter.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axilwr2wbsp #(
+ // {{{
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ADDR_WIDTH = 28,
+ localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AW = C_AXI_ADDR_WIDTH-AXI_LSBS,
+ parameter LGFIFO = 3,
+ localparam DW = C_AXI_DATA_WIDTH
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk,
+ input wire i_axi_reset_n,
+ // AXI write address channel signals
+ // {{{
+ input wire i_axi_awvalid,
+ output reg o_axi_awready,
+ input wire [AW+1:0] i_axi_awaddr,
+ input wire [2:0] i_axi_awprot,
+ // }}}
+ // AXI write data channel signals
+ // {{{
+ input wire i_axi_wvalid,
+ output reg o_axi_wready,
+ input wire [DW-1:0] i_axi_wdata,
+ input wire [DW/8-1:0] i_axi_wstrb,
+ // }}}
+ // AXI write response channel signals
+ // {{{
+ output reg o_axi_bvalid,
+ input wire i_axi_bready,
+ output reg [1:0] o_axi_bresp,
+ // }}}
+ // Wishbone signals
+ // {{{
+ // We'll share the clock and the reset
+ output reg o_wb_cyc,
+ output reg o_wb_stb,
+ output reg [(AW-1):0] o_wb_addr,
+ output reg [(DW-1):0] o_wb_data,
+ output reg [(DW/8-1):0] o_wb_sel,
+ input wire i_wb_ack,
+ input wire i_wb_stall,
+ input wire i_wb_err
+ // }}}
+`ifdef FORMAL
+ // {{{
+ // Output connections only used in formal mode
+ , output wire [LGFIFO:0] f_first,
+ output wire [LGFIFO:0] f_mid,
+ output wire [LGFIFO:0] f_last,
+ output wire [1:0] f_wpending
+ // }}}
+`endif
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ wire w_reset;
+ assign w_reset = (!i_axi_reset_n);
+
+ reg r_awvalid, r_wvalid;
+ reg [AW-1:0] r_addr;
+ reg [DW-1:0] r_data;
+ reg [DW/8-1:0] r_sel;
+
+ reg fifo_full, fifo_empty;
+
+ reg [LGFIFO:0] r_first, r_mid, r_last;
+ wire [LGFIFO:0] next_first, next_last;
+ reg wb_pending;
+ reg [LGFIFO:0] wb_outstanding;
+
+ reg [LGFIFO:0] err_loc;
+ reg err_state;
+
+ wire axi_write_accepted, pending_axi_write;
+ // }}}
+
+ assign pending_axi_write =
+ ((r_awvalid) || (i_axi_awvalid && o_axi_awready))
+ &&((r_wvalid)|| (i_axi_wvalid && o_axi_wready));
+
+ assign axi_write_accepted =
+ (!o_wb_stb || !i_wb_stall) && (!fifo_full) && (!err_state)
+ && (pending_axi_write);
+
+ // o_wb_cyc, o_wb_stb
+ // {{{
+ initial o_wb_cyc = 1'b0;
+ initial o_wb_stb = 1'b0;
+ always @(posedge i_clk)
+ if ((w_reset)||((o_wb_cyc)&&(i_wb_err))||(err_state))
+ o_wb_stb <= 1'b0;
+ else if (axi_write_accepted)
+ o_wb_stb <= 1'b1;
+ else if ((o_wb_cyc)&&(!i_wb_stall))
+ o_wb_stb <= 1'b0;
+
+ always @(*)
+ o_wb_cyc = (wb_pending)||(o_wb_stb);
+ // }}}
+
+ // o_wb_addr, o_wb_data, o_wb_sel
+ // {{{
+ always @(posedge i_clk)
+ if (!o_wb_stb || !i_wb_stall)
+ begin
+ if (r_awvalid)
+ o_wb_addr <= r_addr;
+ else
+ o_wb_addr <= i_axi_awaddr[AW+1:AXI_LSBS];
+
+ if (r_wvalid)
+ begin
+ o_wb_data <= r_data;
+ o_wb_sel <= r_sel;
+ end else begin
+ o_wb_data <= i_axi_wdata;
+ o_wb_sel <= i_axi_wstrb;
+ end
+ end
+ // }}}
+
+ // r_awvalid, r_addr
+ // {{{
+ initial r_awvalid = 1'b0;
+ always @(posedge i_clk)
+ begin
+ if ((i_axi_awvalid)&&(o_axi_awready))
+ begin
+ r_addr <= i_axi_awaddr[AW+1:AXI_LSBS];
+ r_awvalid <= (!axi_write_accepted);
+ end else if (axi_write_accepted)
+ r_awvalid <= 1'b0;
+
+ if (w_reset)
+ r_awvalid <= 1'b0;
+ end
+ // }}}
+
+ // r_wvalid
+ // {{{
+ initial r_wvalid = 1'b0;
+ always @(posedge i_clk)
+ begin
+ if ((i_axi_wvalid)&&(o_axi_wready))
+ begin
+ r_data <= i_axi_wdata;
+ r_sel <= i_axi_wstrb;
+ r_wvalid <= (!axi_write_accepted);
+ end else if (axi_write_accepted)
+ r_wvalid <= 1'b0;
+
+ if (w_reset)
+ r_wvalid <= 1'b0;
+ end
+ // }}}
+
+ // o_axi_awready
+ // {{{
+ initial o_axi_awready = 1'b1;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_awready <= 1'b1;
+ else if ((o_wb_stb && i_wb_stall)
+ &&(r_awvalid || (i_axi_awvalid && o_axi_awready)))
+ // Once a request has been received while the interface is
+ // stalled, we must stall and wait for it to clear
+ o_axi_awready <= 1'b0;
+ else if (err_state && (r_awvalid || (i_axi_awvalid && o_axi_awready)))
+ o_axi_awready <= 1'b0;
+ else if ((r_awvalid || (i_axi_awvalid && o_axi_awready))
+ &&(!r_wvalid && (!i_axi_wvalid || !o_axi_wready)))
+ // If the write address is given without any corresponding
+ // write data, immediately stall and wait for the write data
+ o_axi_awready <= 1'b0;
+ else if (!o_axi_awready && o_wb_stb && i_wb_stall)
+ // Once stalled, remain stalled while the WB bus is stalled
+ o_axi_awready <= 1'b0;
+ else if (fifo_full && (r_awvalid || (!o_axi_bvalid || !i_axi_bready)))
+ // Once the FIFO is full, we must remain stalled until at
+ // least one acknowledgment has been accepted
+ o_axi_awready <= 1'b0;
+ else if ((!o_axi_bvalid || !i_axi_bready)
+ && (r_awvalid || (i_axi_awvalid && o_axi_awready)))
+ // If ever the FIFO becomes full, we must immediately drop
+ // the o_axi_awready signal
+ o_axi_awready <= (next_first[LGFIFO-1:0] != r_last[LGFIFO-1:0])
+ &&(next_first[LGFIFO]==r_last[LGFIFO]);
+ else
+ o_axi_awready <= 1'b1;
+ // }}}
+
+ // o_axi_wready
+ // {{{
+ initial o_axi_wready = 1'b1;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_wready <= 1'b1;
+ else if ((o_wb_stb && i_wb_stall)
+ &&(r_wvalid || (i_axi_wvalid && o_axi_wready)))
+ // Once a request has been received while the interface is
+ // stalled, we must stall and wait for it to clear
+ o_axi_wready <= 1'b0;
+ else if (err_state && (r_wvalid || (i_axi_wvalid && o_axi_wready)))
+ o_axi_wready <= 1'b0;
+ else if ((r_wvalid || (i_axi_wvalid && o_axi_wready))
+ &&(!r_awvalid && (!i_axi_awvalid || !o_axi_awready)))
+ // If the write address is given without any corresponding
+ // write data, immediately stall and wait for the write data
+ o_axi_wready <= 1'b0;
+ else if (!o_axi_wready && o_wb_stb && i_wb_stall)
+ // Once stalled, remain stalled while the WB bus is stalled
+ o_axi_wready <= 1'b0;
+ else if (fifo_full && (r_wvalid || (!o_axi_bvalid || !i_axi_bready)))
+ // Once the FIFO is full, we must remain stalled until at
+ // least one acknowledgment has been accepted
+ o_axi_wready <= 1'b0;
+ else if ((!o_axi_bvalid || !i_axi_bready)
+ && (i_axi_wvalid && o_axi_wready))
+ // If ever the FIFO becomes full, we must immediately drop
+ // the o_axi_wready signal
+ o_axi_wready <= (next_first[LGFIFO-1:0] != r_last[LGFIFO-1:0])
+ &&(next_first[LGFIFO]==r_last[LGFIFO]);
+ else
+ o_axi_wready <= 1'b1;
+ // }}}
+
+ // wb_pending, wb_outstanding
+ // {{{
+ initial wb_pending = 0;
+ initial wb_outstanding = 0;
+ always @(posedge i_clk)
+ if ((w_reset)||(!o_wb_cyc)||(i_wb_err)||(err_state))
+ begin
+ wb_pending <= 1'b0;
+ wb_outstanding <= 0;
+ end else case({ (o_wb_stb)&&(!i_wb_stall), i_wb_ack })
+ 2'b01: begin
+ wb_outstanding <= wb_outstanding - 1'b1;
+ wb_pending <= (wb_outstanding >= 2);
+ end
+ 2'b10: begin
+ wb_outstanding <= wb_outstanding + 1'b1;
+ wb_pending <= 1'b1;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ assign next_first = r_first + 1'b1;
+ assign next_last = r_last + 1'b1;
+
+ // fifo_full, fifo_empty
+ // {{{
+ initial fifo_full = 1'b0;
+ initial fifo_empty = 1'b1;
+ always @(posedge i_clk)
+ if (w_reset)
+ begin
+ fifo_full <= 1'b0;
+ fifo_empty <= 1'b1;
+ end else case({ (o_axi_bvalid)&&(i_axi_bready),
+ (axi_write_accepted) })
+ 2'b01: begin
+ fifo_full <= (next_first[LGFIFO-1:0] == r_last[LGFIFO-1:0])
+ &&(next_first[LGFIFO]!=r_last[LGFIFO]);
+ fifo_empty <= 1'b0;
+ end
+ 2'b10: begin
+ fifo_full <= 1'b0;
+ fifo_empty <= 1'b0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // r_first
+ // {{{
+ initial r_first = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_first <= 0;
+ else if (axi_write_accepted)
+ r_first <= r_first + 1'b1;
+ // }}}
+
+ // r_mid
+ // {{{
+ initial r_mid = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_mid <= 0;
+ else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
+ r_mid <= r_mid + 1'b1;
+ else if ((err_state)&&(r_mid != r_first))
+ r_mid <= r_mid + 1'b1;
+ // }}}
+
+ // r_last
+ // {{{
+ initial r_last = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ r_last <= 0;
+ else if ((o_axi_bvalid)&&(i_axi_bready))
+ r_last <= r_last + 1'b1;
+ // }}}
+
+ // err_loc
+ // {{{
+ always @(posedge i_clk)
+ if ((o_wb_cyc)&&(i_wb_err))
+ err_loc <= r_mid;
+ // }}}
+
+ // o_axi_bresp
+ // {{{
+ initial o_axi_bresp = 2'b00;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_bresp <= 0;
+ else if ((!o_axi_bvalid)||(i_axi_bready))
+ begin
+ if ((!err_state)&&((!o_wb_cyc)||(!i_wb_err)))
+ o_axi_bresp <= 2'b00;
+ else if ((!err_state)&&(o_wb_cyc)&&(i_wb_err))
+ begin
+ if (o_axi_bvalid)
+ o_axi_bresp <= (r_mid == next_last) ? 2'b10 : 2'b00;
+ else
+ o_axi_bresp <= (r_mid == r_last) ? 2'b10 : 2'b00;
+ end else if (err_state)
+ begin
+ if (next_last == err_loc)
+ o_axi_bresp <= 2'b10;
+ else if (o_axi_bresp[1])
+ o_axi_bresp <= 2'b11;
+ end else
+ o_axi_bresp <= 0;
+ end
+ // }}}
+
+ // err_state
+ // {{{
+ initial err_state = 0;
+ always @(posedge i_clk)
+ if (w_reset)
+ err_state <= 0;
+ else if (r_first == r_last)
+ err_state <= 0;
+ else if ((o_wb_cyc)&&(i_wb_err))
+ err_state <= 1'b1;
+ // }}}
+
+ // o_axi_bvalid
+ // {{{
+ initial o_axi_bvalid = 1'b0;
+ always @(posedge i_clk)
+ if (w_reset)
+ o_axi_bvalid <= 0;
+ else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
+ o_axi_bvalid <= 1'b1;
+ else if ((o_axi_bvalid)&&(i_axi_bready))
+ begin
+ if (err_state)
+ o_axi_bvalid <= (next_last != r_first);
+ else
+ o_axi_bvalid <= (next_last != r_mid);
+ end
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, i_axi_awprot,
+ fifo_empty, i_axi_awaddr[AXI_LSBS-1:0] };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ wire f_axi_stalled;
+ wire [LGFIFO:0] f_wb_nreqs, f_wb_nacks, f_wb_outstanding;
+ wire [LGFIFO:0] wb_fill;
+ wire [LGFIFO:0] f_axi_rd_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_awr_outstanding;
+ wire [LGFIFO:0] f_mid_minus_err, f_err_minus_last,
+ f_first_minus_err;
+ wire [LGFIFO:0] f_fifo_fill;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+`ifdef AXILWR2WBSP
+`define ASSUME assume
+`else
+`define ASSUME assert
+`endif
+
+ always @(*)
+ if (!f_past_valid)
+ `ASSUME(w_reset);
+
+ assign f_fifo_fill = (r_first - r_last);
+
+ always @(*)
+ if (err_state)
+ begin
+ assert(!r_awvalid || !o_axi_awready);
+ assert(!r_wvalid || !o_axi_wready);
+
+ assert(!o_wb_cyc);
+ end
+
+ always @(*)
+ if ((fifo_empty)&&(!w_reset))
+ assert((!fifo_full)&&(r_first == r_last)&&(r_mid == r_last));
+
+ always @(*)
+ if (fifo_full)
+ begin
+ assert(!fifo_empty);
+ assert(r_first[LGFIFO-1:0] == r_last[LGFIFO-1:0]);
+ assert(r_first[LGFIFO] != r_last[LGFIFO]);
+ end
+
+ always @(*)
+ assert(f_fifo_fill <= (1<<LGFIFO));
+
+ always @(*)
+ if (fifo_full)
+ begin
+ assert(!r_awvalid || !o_axi_awready);
+ assert(!r_wvalid || !o_axi_wready);
+ end
+
+ always @(*)
+ assert(fifo_full == (f_fifo_fill >= (1<<LGFIFO)));
+ always @(*)
+ assert(wb_pending == (wb_outstanding != 0));
+
+
+ assign f_first = r_first;
+ assign f_mid = r_mid;
+ assign f_last = r_last;
+ assign f_wpending = { r_awvalid, r_wvalid };
+
+ fwb_master #(
+ .AW(AW), .DW(DW), .F_LGDEPTH(LGFIFO+1)
+ ) fwb(i_clk, w_reset,
+ o_wb_cyc, o_wb_stb, 1'b1, o_wb_addr, o_wb_data, o_wb_sel,
+ i_wb_ack, i_wb_stall, {(DW){1'b0}}, i_wb_err,
+ f_wb_nreqs,f_wb_nacks, f_wb_outstanding);
+
+ always @(*)
+ if (o_wb_cyc)
+ assert(f_wb_outstanding == wb_outstanding);
+
+ always @(*)
+ if (o_wb_cyc)
+ assert(wb_outstanding <= (1<<LGFIFO));
+
+ assign wb_fill = r_first - r_mid;
+ always @(*)
+ assert(wb_fill <= f_fifo_fill);
+ always @(*)
+ if (!w_reset)
+ begin
+ if (o_wb_stb)
+ begin
+ assert(wb_outstanding+1 == wb_fill);
+ end else if (o_wb_cyc)
+ begin
+ assert(wb_outstanding == wb_fill);
+ end else if (!err_state)
+ assert((wb_fill == 0)&&(wb_outstanding == 0));
+ end
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(LGFIFO+1),
+ .F_OPT_WRITE_ONLY(1),
+ .F_AXI_MAXWAIT(0),
+ .F_AXI_MAXDELAY(0)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
+ //
+ // AXI write address channel signals
+ .i_axi_awvalid(i_axi_awvalid), .i_axi_awready(o_axi_awready),
+ .i_axi_awaddr(i_axi_awaddr),.i_axi_awprot(i_axi_awprot),
+ // AXI write data channel signals
+ .i_axi_wvalid(i_axi_wvalid), .i_axi_wready(o_axi_wready),
+ .i_axi_wdata(i_axi_wdata), .i_axi_wstrb(i_axi_wstrb),
+ // AXI write response channel signals
+ .i_axi_bvalid(o_axi_bvalid), .i_axi_bready(i_axi_bready),
+ .i_axi_bresp(o_axi_bresp),
+ // AXI read address channel signals
+ .i_axi_arvalid(1'b0), .i_axi_arready(1'b0),
+ .i_axi_araddr(i_axi_awaddr),.i_axi_arprot(i_axi_awprot),
+ // AXI read data channel signals
+ .i_axi_rvalid(1'b0), .i_axi_rready(1'b0),
+ .i_axi_rdata({(DW){1'b0}}),
+ .i_axi_rresp(2'b00),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding),
+ .f_axi_wr_outstanding(f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ assert(f_axi_wr_outstanding - (r_wvalid ? 1:0)
+ == f_axi_awr_outstanding - (r_awvalid ? 1:0));
+ always @(*)
+ assert(f_axi_rd_outstanding == 0);
+ always @(*)
+ assert(f_axi_wr_outstanding - (r_wvalid ? 1:0) == f_fifo_fill);
+ always @(*)
+ assert(f_axi_awr_outstanding - (r_awvalid ? 1:0) == f_fifo_fill);
+ always @(*)
+ if (r_wvalid)
+ assert(f_axi_wr_outstanding > 0);
+
+ always @(*)
+ if (r_awvalid)
+ assert(f_axi_awr_outstanding > 0);
+
+ assign f_mid_minus_err = f_mid - err_loc;
+ assign f_err_minus_last = err_loc - f_last;
+ assign f_first_minus_err = f_first - err_loc;
+ always @(*)
+ if (o_axi_bvalid)
+ begin
+ if (!err_state)
+ begin
+ assert(!o_axi_bresp[1]);
+ end else if (err_loc == f_last)
+ begin
+ assert(o_axi_bresp == 2'b10);
+ end else if (f_err_minus_last < (1<<LGFIFO))
+ begin
+ assert(!o_axi_bresp[1]);
+ end else
+ assert(o_axi_bresp[1]);
+ end
+
+ always @(*)
+ if (err_state)
+ begin
+ assert(o_axi_bvalid == (r_first != r_last));
+ end else
+ assert(o_axi_bvalid == (r_mid != r_last));
+
+ always @(*)
+ if (err_state)
+ assert(f_first_minus_err <= (1<<LGFIFO));
+
+ always @(*)
+ if (err_state)
+ assert(f_first_minus_err != 0);
+
+ always @(*)
+ if (err_state)
+ assert(f_mid_minus_err <= f_first_minus_err);
+
+ assign f_axi_stalled = (fifo_full)||(err_state)
+ ||((o_wb_stb)&&(i_wb_stall));
+
+ always @(*)
+ if ((r_awvalid)&&(f_axi_stalled))
+ assert(!o_axi_awready);
+ always @(*)
+ if ((r_wvalid)&&(f_axi_stalled))
+ assert(!o_axi_wready);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover property checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ cover(o_wb_cyc && o_wb_stb && !i_wb_stall);
+ always @(*)
+ cover(o_wb_cyc && i_wb_ack);
+
+ always @(posedge i_clk)
+ cover(o_wb_cyc && $past(o_wb_cyc && o_wb_stb && !i_wb_stall));//
+
+ always @(posedge i_clk)
+ cover(o_wb_cyc && o_wb_stb && !i_wb_stall
+ && $past(o_wb_cyc && o_wb_stb && !i_wb_stall,2)
+ && $past(o_wb_cyc && o_wb_stb && !i_wb_stall,4)); //
+
+ always @(posedge i_clk)
+ cover(o_wb_cyc && o_wb_stb && !i_wb_stall
+ && $past(o_wb_cyc && o_wb_stb && !i_wb_stall)
+ && $past(o_wb_cyc && o_wb_stb && !i_wb_stall)); //
+
+ always @(posedge i_clk)
+ cover(o_wb_cyc && i_wb_ack
+ && $past(o_wb_cyc && i_wb_ack)
+ && $past(o_wb_cyc && i_wb_ack)); //
+
+ // AXI covers
+ always @(posedge i_clk)
+ cover(o_axi_bvalid && i_axi_bready
+ && $past(o_axi_bvalid && i_axi_bready,1)
+ && $past(o_axi_bvalid && i_axi_bready,2)); //
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_formal;
+ assign unused_formal = &{ 1'b0, f_wb_nreqs, f_wb_nacks };
+ // Verilator lint_on UNUSED
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axilxbar.v b/rtl/wb2axip/axilxbar.v
new file mode 100644
index 0000000..4a1ed43
--- /dev/null
+++ b/rtl/wb2axip/axilxbar.v
@@ -0,0 +1,2421 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilxbar.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Create a full crossbar between NM AXI-lite sources (masters),
+// and NS AXI-lite slaves. Every master can talk to any slave,
+// provided it isn't already busy.
+//
+// Performance: This core has been designed with the goal of being able to push
+// one transaction through the interconnect, from any master to
+// any slave, per clock cycle. This may perhaps be its most unique
+// feature. While throughput is good, latency is something else.
+//
+// The arbiter requires a clock to switch, then another clock to send data
+// downstream. This creates a minimum two clock latency up front. The
+// return path suffers another clock of latency as well, placing the
+// minimum latency at four clocks. The minimum write latency is at
+// least one clock longer, since the write data must wait for the write
+// address before proceeeding.
+//
+// Usage: To use, you must first set NM and NS to the number of masters
+// and the number of slaves you wish to connect to. You then need to
+// adjust the addresses of the slaves, found SLAVE_ADDR array. Those
+// bits that are relevant in SLAVE_ADDR to then also be set in SLAVE_MASK.
+// Adjusting the data and address widths go without saying.
+//
+// Lower numbered masters are given priority in any "fight".
+//
+// Channel grants are given on the condition that 1) they are requested,
+// 2) no other channel has a grant, 3) all of the responses have been
+// received from the current channel, and 4) the internal counters are
+// not overflowing.
+//
+// The core limits the number of outstanding transactions on any channel to
+// 1<<LGMAXBURST-1.
+//
+// Channel grants are lost 1) after OPT_LINGER clocks of being idle, or
+// 2) when another master requests an idle (but still lingering) channel
+// assignment, or 3) once all the responses have been returned to the
+// current channel, and the current master is requesting another channel.
+//
+// A special slave is allocated for the case of no valid address.
+//
+// Since the write channel has no address information, the write data
+// channel always be delayed by at least one clock from the write address
+// channel.
+//
+// If OPT_LOWPOWER is set, then unused values will be set to zero.
+// This can also be used to help identify relevant values within any
+// trace.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axilxbar #(
+ // {{{
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 32,
+ //
+ // NM is the number of master interfaces this core supports
+ parameter NM = 4,
+ //
+ // NS is the number of slave interfaces
+ parameter NS = 8,
+ //
+ // AW, and DW, are short-hand abbreviations used locally.
+ localparam AW = C_AXI_ADDR_WIDTH,
+ localparam DW = C_AXI_DATA_WIDTH,
+ // SLAVE_ADDR is a bit vector containing AW bits for each of the
+ // slaves indicating the base address of the slave. This
+ // goes with SLAVE_MASK below.
+ parameter [NS*AW-1:0] SLAVE_ADDR = {
+ 3'b111, {(AW-3){1'b0}},
+ 3'b110, {(AW-3){1'b0}},
+ 3'b101, {(AW-3){1'b0}},
+ 3'b100, {(AW-3){1'b0}},
+ 3'b011, {(AW-3){1'b0}},
+ 3'b010, {(AW-3){1'b0}},
+ 4'b0001, {(AW-4){1'b0}},
+ 4'b0000, {(AW-4){1'b0}} },
+ //
+ // SLAVE_MASK indicates which bits in the SLAVE_ADDR bit vector
+ // need to be checked to determine if a given address request
+ // maps to the given slave or not
+ // Verilator lint_off WIDTH
+ parameter [NS*AW-1:0] SLAVE_MASK =
+ (NS <= 1) ? { 4'b1111, {(AW-4){1'b0}} }
+ : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }},
+ {(2){ 4'b1111, {(AW-4){1'b0}} }} },
+ // Verilator lint_on WIDTH
+ //
+ // If set, OPT_LOWPOWER will set all unused registers, both
+ // internal and external, to zero anytime their corresponding
+ // *VALID bit is clear
+ parameter [0:0] OPT_LOWPOWER = 1,
+ //
+ // OPT_LINGER is the number of cycles to wait, following a
+ // transaction, before tearing down the bus grant.
+ parameter OPT_LINGER = 4,
+ //
+ // LGMAXBURST is the log (base two) of the maximum number of
+ // requests that can be outstanding on any given channel at any
+ // given time. It is used within this core to control the
+ // counters that are used to determine if a particular channel
+ // grant must stay open, or if it may be closed.
+ parameter LGMAXBURST = 5
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // Incoming AXI4-lite slave port(s)
+ // {{{
+ input wire [NM-1:0] S_AXI_AWVALID,
+ output wire [NM-1:0] S_AXI_AWREADY,
+ input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ // Verilator coverage_off
+ input wire [NM*3-1:0] S_AXI_AWPROT,
+ // Verilator coverage_on
+ //
+ input wire [NM-1:0] S_AXI_WVALID,
+ output wire [NM-1:0] S_AXI_WREADY,
+ input wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [NM*C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire [NM-1:0] S_AXI_BVALID,
+ input wire [NM-1:0] S_AXI_BREADY,
+ output wire [NM*2-1:0] S_AXI_BRESP,
+ //
+ input wire [NM-1:0] S_AXI_ARVALID,
+ output wire [NM-1:0] S_AXI_ARREADY,
+ input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ // Verilator coverage_off
+ input wire [NM*3-1:0] S_AXI_ARPROT,
+ // Verilator coverage_on
+ //
+ output wire [NM-1:0] S_AXI_RVALID,
+ input wire [NM-1:0] S_AXI_RREADY,
+ output wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [NM*2-1:0] S_AXI_RRESP,
+ // }}}
+ // Outgoing AXI4-lite master port(s)
+ // {{{
+ output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [NS*3-1:0] M_AXI_AWPROT,
+ output wire [NS-1:0] M_AXI_AWVALID,
+ input wire [NS-1:0] M_AXI_AWREADY,
+ //
+ output wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [NS*C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire [NS-1:0] M_AXI_WVALID,
+ input wire [NS-1:0] M_AXI_WREADY,
+ //
+ input wire [NS*2-1:0] M_AXI_BRESP,
+ input wire [NS-1:0] M_AXI_BVALID,
+ output wire [NS-1:0] M_AXI_BREADY,
+ //
+ output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [NS*3-1:0] M_AXI_ARPROT,
+ output wire [NS-1:0] M_AXI_ARVALID,
+ input wire [NS-1:0] M_AXI_ARREADY,
+ //
+ input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [NS*2-1:0] M_AXI_RRESP,
+ input wire [NS-1:0] M_AXI_RVALID,
+ output wire [NS-1:0] M_AXI_RREADY
+ // }}}
+ // }}}
+ );
+ //
+ // Local parameters, derived from those above
+ // {{{
+ localparam LGLINGER = (OPT_LINGER>1) ? $clog2(OPT_LINGER+1) : 1;
+ //
+ localparam LGNM = (NM>1) ? $clog2(NM) : 1;
+ localparam LGNS = (NS>1) ? $clog2(NS+1) : 1;
+ //
+ // In order to use indexes, and hence fully balanced mux trees, it helps
+ // to make certain that we have a power of two based lookup. NMFULL
+ // is the number of masters in this lookup, with potentially some
+ // unused extra ones. NSFULL is defined similarly.
+ localparam NMFULL = (NM>1) ? (1<<LGNM) : 1;
+ localparam NSFULL = (NS>1) ? (1<<LGNS) : 2;
+ //
+ localparam [1:0] INTERCONNECT_ERROR = 2'b11;
+ localparam [0:0] OPT_SKID_INPUT = 0;
+ localparam [0:0] OPT_BUFFER_DECODER = 1;
+
+ genvar N,M;
+ integer iN, iM;
+ // }}}
+
+ // {{{
+ reg [NSFULL-1:0] wrequest [0:NM-1];
+ reg [NSFULL-1:0] rrequest [0:NM-1];
+ reg [NSFULL-1:0] wrequested [0:NM];
+ reg [NSFULL-1:0] rrequested [0:NM];
+ reg [NS:0] wgrant [0:NM-1];
+ reg [NS:0] rgrant [0:NM-1];
+ reg [NM-1:0] swgrant;
+ reg [NM-1:0] srgrant;
+ reg [NS-1:0] mwgrant;
+ reg [NS-1:0] mrgrant;
+
+ // verilator lint_off UNUSED
+ wire [LGMAXBURST-1:0] w_sawpending [0:NM-1];
+`ifdef FORMAL
+ wire [LGMAXBURST-1:0] w_swpending [0:NM-1];
+`endif
+ wire [LGMAXBURST-1:0] w_srpending [0:NM-1];
+ // verilator lint_on UNUSED
+ reg [NM-1:0] swfull;
+ reg [NM-1:0] srfull;
+ reg [NM-1:0] swempty;
+ reg [NM-1:0] srempty;
+ //
+ wire [LGNS-1:0] swindex [0:NMFULL-1];
+ wire [LGNS-1:0] srindex [0:NMFULL-1];
+ wire [LGNM-1:0] mwindex [0:NSFULL-1];
+ wire [LGNM-1:0] mrindex [0:NSFULL-1];
+
+ wire [NM-1:0] wdata_expected;
+
+ // The shadow buffers
+ wire [NMFULL-1:0] m_awvalid, m_wvalid, m_arvalid;
+ wire [NM-1:0] dcd_awvalid, dcd_arvalid;
+
+ wire [C_AXI_ADDR_WIDTH-1:0] m_awaddr [0:NMFULL-1];
+ wire [2:0] m_awprot [0:NMFULL-1];
+ wire [C_AXI_DATA_WIDTH-1:0] m_wdata [0:NMFULL-1];
+ wire [C_AXI_DATA_WIDTH/8-1:0] m_wstrb [0:NMFULL-1];
+
+ wire [C_AXI_ADDR_WIDTH-1:0] m_araddr [0:NMFULL-1];
+ wire [2:0] m_arprot [0:NMFULL-1];
+ //
+
+ wire [NM-1:0] skd_awvalid, skd_awstall, skd_wvalid;
+ wire [NM-1:0] skd_arvalid, skd_arstall;
+ wire [AW-1:0] skd_awaddr [0:NM-1];
+ wire [3-1:0] skd_awprot [0:NM-1];
+ wire [AW-1:0] skd_araddr [0:NM-1];
+ wire [3-1:0] skd_arprot [0:NM-1];
+
+ reg [NM-1:0] r_bvalid;
+ reg [1:0] r_bresp [0:NM-1];
+
+ reg [NSFULL-1:0] m_axi_awvalid;
+ reg [NSFULL-1:0] m_axi_awready;
+ reg [NSFULL-1:0] m_axi_wvalid;
+ reg [NSFULL-1:0] m_axi_wready;
+ reg [NSFULL-1:0] m_axi_bvalid;
+`ifdef FORMAL
+ reg [NSFULL-1:0] m_axi_bready;
+`endif
+ reg [1:0] m_axi_bresp [0:NSFULL-1];
+
+ reg [NSFULL-1:0] m_axi_arvalid;
+ // Verilator lint_off UNUSED
+ reg [NSFULL-1:0] m_axi_arready;
+ // Verilator lint_on UNUSED
+ reg [NSFULL-1:0] m_axi_rvalid;
+ // Verilator lint_off UNUSED
+ reg [NSFULL-1:0] m_axi_rready;
+ // Verilator lint_on UNUSED
+
+ reg [NM-1:0] r_rvalid;
+ reg [1:0] r_rresp [0:NM-1];
+ reg [DW-1:0] r_rdata [0:NM-1];
+
+ reg [DW-1:0] m_axi_rdata [0:NSFULL-1];
+ reg [1:0] m_axi_rresp [0:NSFULL-1];
+
+ reg [NM-1:0] slave_awaccepts;
+ reg [NM-1:0] slave_waccepts;
+ reg [NM-1:0] slave_raccepts;
+ // }}}
+
+ // m_axi_[aw|w|b]*
+ // {{{
+ always @(*)
+ begin
+ m_axi_awvalid = -1;
+ m_axi_awready = -1;
+ m_axi_wvalid = -1;
+ m_axi_wready = -1;
+ m_axi_bvalid = 0;
+
+ m_axi_awvalid[NS-1:0] = M_AXI_AWVALID;
+ m_axi_awready[NS-1:0] = M_AXI_AWREADY;
+ m_axi_wvalid[NS-1:0] = M_AXI_WVALID;
+ m_axi_wready[NS-1:0] = M_AXI_WREADY;
+ m_axi_bvalid[NS-1:0] = M_AXI_BVALID;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ m_axi_bresp[iM] = M_AXI_BRESP[iM* 2 +: 2];
+
+ m_axi_rdata[iM] = M_AXI_RDATA[iM*DW +: DW];
+ m_axi_rresp[iM] = M_AXI_RRESP[iM* 2 +: 2];
+ end
+ for(iM=NS; iM<NSFULL; iM=iM+1)
+ begin
+ m_axi_bresp[iM] = INTERCONNECT_ERROR;
+
+ m_axi_rdata[iM] = 0;
+ m_axi_rresp[iM] = INTERCONNECT_ERROR;
+ end
+
+`ifdef FORMAL
+ m_axi_bready = -1;
+ m_axi_bready[NS-1:0] = M_AXI_BREADY;
+`endif
+ end
+ // }}}
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : DECODE_WRITE_REQUEST
+ // {{{
+ wire [NS:0] wdecode;
+ reg r_mawvalid, r_mwvalid;
+
+ // awskid
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(AW+3), .OPT_OUTREG(OPT_SKID_INPUT)
+ // }}}
+ ) awskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID[N]), .o_ready(S_AXI_AWREADY[N]),
+ .i_data({ S_AXI_AWADDR[N*AW +: AW], S_AXI_AWPROT[N*3 +: 3] }),
+ .o_valid(skd_awvalid[N]), .i_ready(!skd_awstall[N]),
+ .o_data({ skd_awaddr[N], skd_awprot[N] })
+ // }}}
+ );
+ // }}}
+
+ // write address decoding
+ // {{{
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(3), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(OPT_BUFFER_DECODER)
+ // }}}
+ ) wraddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(skd_awvalid[N]), .o_stall(skd_awstall[N]),
+ .i_addr(skd_awaddr[N]), .i_data(skd_awprot[N]),
+ .o_valid(dcd_awvalid[N]),
+ .i_stall(!dcd_awvalid[N]||!slave_awaccepts[N]),
+ .o_decode(wdecode), .o_addr(m_awaddr[N]),
+ .o_data(m_awprot[N])
+ // }}}
+ );
+ // }}}
+
+ // wskid
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(DW+DW/8), .OPT_OUTREG(OPT_SKID_INPUT)
+ // }}}
+ ) wskid (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_WVALID[N]), .o_ready(S_AXI_WREADY[N]),
+ .i_data({ S_AXI_WDATA[N*DW +: DW],
+ S_AXI_WSTRB[N*DW/8 +: DW/8]}),
+ .o_valid(skd_wvalid[N]),
+ .i_ready(m_wvalid[N] && slave_waccepts[N]),
+ .o_data({ m_wdata[N], m_wstrb[N] })
+ // }}}
+ );
+ // }}}
+
+ // slave_awaccepts
+ // {{{
+ always @(*)
+ begin
+ slave_awaccepts[N] = 1'b1;
+ if (!swgrant[N])
+ slave_awaccepts[N] = 1'b0;
+ if (swfull[N])
+ slave_awaccepts[N] = 1'b0;
+ if (!wrequest[N][swindex[N]])
+ slave_awaccepts[N] = 1'b0;
+ if (!wgrant[N][NS]&&(m_axi_awvalid[swindex[N]] && !m_axi_awready[swindex[N]]))
+ slave_awaccepts[N] = 1'b0;
+ // ERRORs are always accepted
+ // back pressure is handled in the write side
+ end
+ // }}}
+
+ // slave_waccepts
+ // {{{
+ always @(*)
+ begin
+ slave_waccepts[N] = 1'b1;
+ if (!swgrant[N])
+ slave_waccepts[N] = 1'b0;
+ if (!wdata_expected[N])
+ slave_waccepts[N] = 1'b0;
+ if (!wgrant[N][NS] &&(m_axi_wvalid[swindex[N]]
+ && !m_axi_wready[swindex[N]]))
+ slave_waccepts[N] = 1'b0;
+ if (wgrant[N][NS]&&(S_AXI_BVALID[N]&& !S_AXI_BREADY[N]))
+ slave_waccepts[N] = 1'b0;
+ end
+ // }}}
+
+ // r_mawvalid, r_mwvalid
+ // {{{
+ always @(*)
+ begin
+ r_mawvalid= dcd_awvalid[N] && !swfull[N];
+ r_mwvalid = skd_wvalid[N];
+ wrequest[N]= 0;
+ if (!swfull[N])
+ wrequest[N][NS:0] = wdecode;
+ end
+
+ assign m_awvalid[N] = r_mawvalid;
+ assign m_wvalid[N] = r_mwvalid;
+ // }}}
+
+ // }}}
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_WSKID_BUFFERS
+ // {{{
+ assign m_awvalid[N] = 0;
+ assign m_awaddr[N] = 0;
+ assign m_awprot[N] = 0;
+ assign m_wdata[N] = 0;
+ assign m_wstrb[N] = 0;
+ // }}}
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : DECODE_READ_REQUEST
+ // {{{
+ wire [NS:0] rdecode;
+ reg r_marvalid;
+
+ // arskid
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(AW+3), .OPT_OUTREG(OPT_SKID_INPUT)
+ // }}}
+ ) arskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID[N]), .o_ready(S_AXI_ARREADY[N]),
+ .i_data({ S_AXI_ARADDR[N*AW +: AW], S_AXI_ARPROT[N*3 +: 3] }),
+ .o_valid(skd_arvalid[N]), .i_ready(!skd_arstall[N]),
+ .o_data({ skd_araddr[N], skd_arprot[N] })
+ // }}}
+ );
+ // }}}
+
+ // Read address decoding
+ // {{{
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(3), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR), .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(OPT_BUFFER_DECODER)
+ // }}}
+ ) rdaddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(skd_arvalid[N]), .o_stall(skd_arstall[N]),
+ .i_addr(skd_araddr[N]), .i_data(skd_arprot[N]),
+ .o_valid(dcd_arvalid[N]),
+ .i_stall(!m_arvalid[N] || !slave_raccepts[N]),
+ .o_decode(rdecode), .o_addr(m_araddr[N]),
+ .o_data(m_arprot[N])
+ // }}}
+ );
+ // }}}
+
+ // r_marvalid -> m_arvalid[N]
+ // {{{
+ always @(*)
+ begin
+ r_marvalid = dcd_arvalid[N] && !srfull[N];
+ rrequest[N] = 0;
+ if (!srfull[N])
+ rrequest[N][NS:0] = rdecode;
+ end
+
+ assign m_arvalid[N] = r_marvalid;
+ // }}}
+
+ // slave_raccepts
+ // {{{
+ always @(*)
+ begin
+ slave_raccepts[N] = 1'b1;
+ if (!srgrant[N])
+ slave_raccepts[N] = 1'b0;
+ if (srfull[N])
+ slave_raccepts[N] = 1'b0;
+ // verilator lint_off WIDTH
+ if (!rrequest[N][srindex[N]])
+ slave_raccepts[N] = 1'b0;
+ // verilator lint_on WIDTH
+ if (!rgrant[N][NS])
+ begin
+ if (m_axi_arvalid[srindex[N]] && !m_axi_arready[srindex[N]])
+ slave_raccepts[N] = 1'b0;
+ end else if (S_AXI_RVALID[N] && !S_AXI_RREADY[N])
+ slave_raccepts[N] = 1'b0;
+ end
+ // }}}
+
+ // }}}
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_RSKID_BUFFERS
+ // {{{
+ assign m_arvalid[N] = 0;
+ assign m_araddr[N] = 0;
+ assign m_arprot[N] = 0;
+ // }}}
+ end endgenerate
+
+ // wrequested
+ // {{{
+ always @(*)
+ begin : DECONFLICT_WRITE_REQUESTS
+
+ for(iN=1; iN<NM ; iN=iN+1)
+ wrequested[iN] = 0;
+
+ // Vivado may complain about too many bits for wrequested.
+ // This is (currrently) expected. swindex is used to index
+ // into wrequested, and swindex has LGNS bits, where LGNS
+ // is $clog2(NS+1) rather than $clog2(NS). The extra bits
+ // are defined to be zeros, but the point is there are defined.
+ // Therefore, no matter what swindex is, it will always
+ // reference something valid.
+ wrequested[NM] = 0;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ wrequested[0][iM] = 1'b0;
+ for(iN=1; iN<NM ; iN=iN+1)
+ begin
+ // Continue to request any channel with
+ // a grant and pending operations
+ if (wrequest[iN-1][iM] && wgrant[iN-1][iM])
+ wrequested[iN][iM] = 1;
+ if (wrequest[iN-1][iM] && (!swgrant[iN-1]||swempty[iN-1]))
+ wrequested[iN][iM] = 1;
+ // Otherwise, if it's already claimed, then
+ // it can't be claimed again
+ if (wrequested[iN-1][iM])
+ wrequested[iN][iM] = 1;
+ end
+ wrequested[NM][iM] = wrequest[NM-1][iM] || wrequested[NM-1][iM];
+ end
+ end
+ // }}}
+
+ // rrequested
+ // {{{
+ always @(*)
+ begin : DECONFLICT_READ_REQUESTS
+
+ for(iN=0; iN<NM ; iN=iN+1)
+ rrequested[iN] = 0;
+
+ // See the note above for wrequested. This applies to
+ // rrequested as well.
+ rrequested[NM] = 0;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ rrequested[0][iM] = 0;
+ for(iN=1; iN<NM ; iN=iN+1)
+ begin
+ // Continue to request any channel with
+ // a grant and pending operations
+ if (rrequest[iN-1][iM] && rgrant[iN-1][iM])
+ rrequested[iN][iM] = 1;
+ if (rrequest[iN-1][iM] && (!srgrant[iN-1] || srempty[iN-1]))
+ rrequested[iN][iM] = 1;
+ // Otherwise, if it's already claimed, then
+ // it can't be claimed again
+ if (rrequested[iN-1][iM])
+ rrequested[iN][iM] = 1;
+ end
+ rrequested[NM][iM] = rrequest[NM-1][iM] || rrequested[NM-1][iM];
+ end
+ end
+ // }}}
+
+ // mwgrant, mrgrant
+ // {{{
+ generate for(M=0; M<NS; M=M+1)
+ begin : GEN_GRANT
+ // {{{
+ initial mwgrant[M] = 0;
+ always @(*)
+ begin
+ mwgrant[M] = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (wgrant[iN][M])
+ mwgrant[M] = 1;
+ end
+
+ always @(*)
+ begin
+ mrgrant[M] = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (rgrant[iN][M])
+ mrgrant[M] = 1;
+ end
+ // }}}
+ end endgenerate
+ // }}}
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : ARBITRATE_WRITE_REQUESTS
+ // {{{
+ // Declarations
+ // {{{
+ reg stay_on_channel;
+ reg requested_channel_is_available;
+ reg leave_channel;
+ reg [LGNS-1:0] requested_index;
+ wire linger;
+ // }}}
+
+ // stay_on_channel
+ // {{{
+ always @(*)
+ begin
+ stay_on_channel = |(wrequest[N][NS:0] & wgrant[N]);
+
+ if (swgrant[N] && !swempty[N])
+ stay_on_channel = 1;
+ end
+ // }}}
+
+ // requested_channel_is_available
+ // {{{
+ always @(*)
+ begin
+ requested_channel_is_available =
+ |(wrequest[N][NS-1:0] & ~mwgrant
+ & ~wrequested[N][NS-1:0]);
+ if (wrequest[N][NS])
+ requested_channel_is_available = 1;
+
+ if (NM < 2)
+ requested_channel_is_available = m_awvalid[N];
+ end
+ // }}}
+
+ if (OPT_LINGER == 0)
+ begin : NO_LINGER
+ // {{{
+ assign linger = 0;
+ // }}}
+ end else begin : WRITE_LINGER
+ // {{{
+ reg [LGLINGER-1:0] linger_counter;
+ reg r_linger;
+
+ initial r_linger = 0;
+ initial linger_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || wgrant[N][NS])
+ begin
+ r_linger <= 0;
+ linger_counter <= 0;
+ end else if (!swempty[N] || S_AXI_BVALID[N])
+ begin
+ linger_counter <= OPT_LINGER;
+ r_linger <= 1;
+ end else if (linger_counter > 0)
+ begin
+ r_linger <= (linger_counter > 1);
+ linger_counter <= linger_counter - 1;
+ end else
+ r_linger <= 0;
+
+ assign linger = r_linger;
+
+`ifdef FORMAL
+ // {{{
+ always @(*)
+ assert(linger == (linger_counter != 0));
+ // }}}
+`endif
+ // }}}
+ end
+
+ // leave_channel
+ // {{{
+ always @(*)
+ begin
+ leave_channel = 0;
+ if (!m_awvalid[N]
+ && (!linger || wrequested[NM][swindex[N]]))
+ // Leave the channel after OPT_LINGER counts
+ // of the channel being idle, or when someone
+ // else asks for the channel
+ leave_channel = 1;
+ if (m_awvalid[N] && !wrequest[N][swindex[N]])
+ // Need to leave this channel to connect
+ // to any other channel
+ leave_channel = 1;
+ end
+ // }}}
+
+ // wgrant, swgrant
+ // {{{
+ initial wgrant[N] = 0;
+ initial swgrant[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ wgrant[N] <= 0;
+ swgrant[N] <= 0;
+ end else if (!stay_on_channel)
+ begin
+ if (requested_channel_is_available)
+ begin
+ // Switching channels
+ swgrant[N] <= 1'b1;
+ wgrant[N] <= wrequest[N][NS:0];
+ end else if (leave_channel)
+ begin
+ swgrant[N] <= 1'b0;
+ wgrant[N] <= 0;
+ end
+ end
+ // }}}
+
+ // requested_index
+ // {{{
+ always @(wrequest[N])
+ begin
+ requested_index = 0;
+ for(iM=0; iM<=NS; iM=iM+1)
+ if (wrequest[N][iM])
+ requested_index= requested_index | iM[LGNS-1:0];
+ end
+ // }}}
+
+ // Now for swindex
+ // {{{
+ reg [LGNS-1:0] r_swindex;
+
+ initial r_swindex = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!stay_on_channel && requested_channel_is_available)
+ r_swindex <= requested_index;
+
+ assign swindex[N] = r_swindex;
+ // }}}
+ // }}}
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : EMPTY_WRITE_REQUEST
+ // {{{
+ assign swindex[N] = 0;
+ // }}}
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : ARBITRATE_READ_REQUESTS
+ // {{{
+ // Declarations
+ // {{{
+ reg stay_on_channel;
+ reg requested_channel_is_available;
+ reg leave_channel;
+ reg [LGNS-1:0] requested_index;
+ wire linger;
+ // }}}
+
+ // stay_on_channel
+ // {{{
+ always @(*)
+ begin
+ stay_on_channel = |(rrequest[N][NS:0] & rgrant[N]);
+
+ if (srgrant[N] && !srempty[N])
+ stay_on_channel = 1;
+ end
+ // }}}
+
+ // requested_channel_is_available
+ // {{{
+ always @(*)
+ begin
+ requested_channel_is_available =
+ |(rrequest[N][NS-1:0] & ~mrgrant
+ & ~rrequested[N][NS-1:0]);
+ if (rrequest[N][NS])
+ requested_channel_is_available = 1;
+
+ if (NM < 2)
+ requested_channel_is_available = m_arvalid[N];
+ end
+ // }}}
+
+ if (OPT_LINGER == 0)
+ begin : NO_LINGER
+ // {{{
+ assign linger = 0;
+ // }}}
+ end else begin : READ_LINGER
+ // {{{
+ reg [LGLINGER-1:0] linger_counter;
+ reg r_linger;
+
+ initial r_linger = 0;
+ initial linger_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || rgrant[N][NS])
+ begin
+ r_linger <= 0;
+ linger_counter <= 0;
+ end else if (!srempty[N] || S_AXI_RVALID[N])
+ begin
+ linger_counter <= OPT_LINGER;
+ r_linger <= 1;
+ end else if (linger_counter > 0)
+ begin
+ r_linger <= (linger_counter > 1);
+ linger_counter <= linger_counter - 1;
+ end else
+ r_linger <= 0;
+
+ assign linger = r_linger;
+`ifdef FORMAL
+ // {{{
+ always @(*)
+ assert(linger == (linger_counter != 0));
+ // }}}
+`endif
+ // }}}
+ end
+
+ // leave_channel
+ // {{{
+ always @(*)
+ begin
+ leave_channel = 0;
+ if (!m_arvalid[N]
+ && (!linger || rrequested[NM][srindex[N]]))
+ // Leave the channel after OPT_LINGER counts
+ // of the channel being idle, or when someone
+ // else asks for the channel
+ leave_channel = 1;
+ if (m_arvalid[N] && !rrequest[N][srindex[N]])
+ // Need to leave this channel to connect
+ // to any other channel
+ leave_channel = 1;
+ end
+ // }}}
+
+ // rgrant, srgrant
+ // {{{
+ initial rgrant[N] = 0;
+ initial srgrant[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rgrant[N] <= 0;
+ srgrant[N] <= 0;
+ end else if (!stay_on_channel)
+ begin
+ if (requested_channel_is_available)
+ begin
+ // Switching channels
+ srgrant[N] <= 1'b1;
+ rgrant[N] <= rrequest[N][NS:0];
+ end else if (leave_channel)
+ begin
+ srgrant[N] <= 1'b0;
+ rgrant[N] <= 0;
+ end
+ end
+ // }}}
+
+ // requested_index
+ // {{{
+ always @(rrequest[N])
+ begin
+ requested_index = 0;
+ for(iM=0; iM<=NS; iM=iM+1)
+ if (rrequest[N][iM])
+ requested_index = requested_index|iM[LGNS-1:0];
+ end
+ // }}}
+
+ // Now for srindex
+ // {{{
+ reg [LGNS-1:0] r_srindex;
+
+ initial r_srindex = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!stay_on_channel && requested_channel_is_available)
+ r_srindex <= requested_index;
+
+ assign srindex[N] = r_srindex;
+ // }}}
+ // }}}
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : EMPTY_READ_REQUEST
+ // {{{
+ assign srindex[N] = 0;
+ // }}}
+ end endgenerate
+
+ // Calculate mwindex
+ generate for (M=0; M<NS; M=M+1)
+ begin : SLAVE_WRITE_INDEX
+ // {{{
+ if (NM <= 1)
+ begin : ONE_MASTER
+ // {{{
+ assign mwindex[M] = 0;
+ // }}}
+ end else begin : MULTIPLE_MASTERS
+ // {{{
+ reg [LGNM-1:0] reswindex;
+ reg [LGNM-1:0] r_mwindex;
+
+ always @(*)
+ begin
+ reswindex = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if ((!swgrant[iN] || swempty[iN])
+ &&(wrequest[iN][M] && !wrequested[iN][M]))
+ reswindex = reswindex | iN[LGNM-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!mwgrant[M])
+ r_mwindex <= reswindex;
+
+ assign mwindex[M] = r_mwindex;
+ // }}}
+ end
+ // }}}
+ end for (M=NS; M<NSFULL; M=M+1)
+ begin : NO_WRITE_INDEX
+ // {{{
+ assign mwindex[M] = 0;
+ // }}}
+ end endgenerate
+
+ // Calculate mrindex
+ generate for (M=0; M<NS; M=M+1)
+ begin : SLAVE_READ_INDEX
+ // {{{
+ if (NM <= 1)
+ begin : ONE_MASTER
+ // {{{
+ assign mrindex[M] = 0;
+ // }}}
+ end else begin : MULTIPLE_MASTERS
+ // {{{
+ reg [LGNM-1:0] resrindex;
+ reg [LGNM-1:0] r_mrindex;
+
+ always @(*)
+ begin
+ resrindex = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if ((!srgrant[iN] || srempty[iN])
+ &&(rrequest[iN][M] && !rrequested[iN][M]))
+ resrindex = resrindex | iN[LGNM-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!mrgrant[M])
+ r_mrindex <= resrindex;
+
+ assign mrindex[M] = r_mrindex;
+ // }}}
+ end
+ // }}}
+ end for (M=NS; M<NSFULL; M=M+1)
+ begin : NO_READ_INDEX
+ // {{{
+ assign mrindex[M] = 0;
+ // }}}
+ end endgenerate
+
+ // Assign outputs to the various slaves
+ generate for(M=0; M<NS; M=M+1)
+ begin : WRITE_SLAVE_OUTPUTS
+ // {{{
+
+ // Declarations
+ // {{{
+ reg axi_awvalid;
+ reg [AW-1:0] axi_awaddr;
+ reg [2:0] axi_awprot;
+
+ reg axi_wvalid;
+ reg [DW-1:0] axi_wdata;
+ reg [DW/8-1:0] axi_wstrb;
+ //
+ reg axi_bready;
+
+ wire sawstall, swstall, mbstall;
+ // }}}
+ assign sawstall= (M_AXI_AWVALID[M]&& !M_AXI_AWREADY[M]);
+ assign swstall = (M_AXI_WVALID[M] && !M_AXI_WREADY[M]);
+ assign mbstall = (S_AXI_BVALID[mwindex[M]] && !S_AXI_BREADY[mwindex[M]]);
+
+ // axi_awvalid
+ // {{{
+ initial axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !mwgrant[M])
+ axi_awvalid <= 0;
+ else if (!sawstall)
+ begin
+ axi_awvalid <= m_awvalid[mwindex[M]]
+ &&(slave_awaccepts[mwindex[M]]);
+ end
+ // }}}
+
+ // axi_awaddr, axi_awprot
+ // {{{
+ initial axi_awaddr = 0;
+ initial axi_awprot = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_awaddr <= 0;
+ axi_awprot <= 0;
+ end else if (OPT_LOWPOWER && !mwgrant[M])
+ begin
+ axi_awaddr <= 0;
+ axi_awprot <= 0;
+ end else if (!sawstall)
+ begin
+ if (!OPT_LOWPOWER||(m_awvalid[mwindex[M]]&&slave_awaccepts[mwindex[M]]))
+ begin
+ axi_awaddr <= m_awaddr[mwindex[M]];
+ axi_awprot <= m_awprot[mwindex[M]];
+ end else begin
+ axi_awaddr <= 0;
+ axi_awprot <= 0;
+ end
+ end
+ // }}}
+
+ // axi_wvalid
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !mwgrant[M])
+ axi_wvalid <= 0;
+ else if (!swstall)
+ begin
+ axi_wvalid <= (m_wvalid[mwindex[M]])
+ &&(slave_waccepts[mwindex[M]]);
+ end
+ // }}}
+
+ // axi_wdata, axi_wstrb
+ // {{{
+ initial axi_wdata = 0;
+ initial axi_wstrb = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ end else if (OPT_LOWPOWER && !mwgrant[M])
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ end else if (!swstall)
+ begin
+ if (!OPT_LOWPOWER || (m_wvalid[mwindex[M]]&&slave_waccepts[mwindex[M]]))
+ begin
+ axi_wdata <= m_wdata[mwindex[M]];
+ axi_wstrb <= m_wstrb[mwindex[M]];
+ end else begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ end
+ end
+ // }}}
+
+ // axi_bready
+ // {{{
+ initial axi_bready = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !mwgrant[M])
+ axi_bready <= 1;
+ else if (!mbstall)
+ axi_bready <= 1;
+ else if (M_AXI_BVALID[M]) // && mbstall
+ axi_bready <= 0;
+ // }}}
+
+ //
+ assign M_AXI_AWVALID[M] = axi_awvalid;
+ assign M_AXI_AWADDR[M*AW +: AW] = axi_awaddr;
+ assign M_AXI_AWPROT[M*3 +: 3] = axi_awprot;
+ //
+ //
+ assign M_AXI_WVALID[M] = axi_wvalid;
+ assign M_AXI_WDATA[M*DW +: DW] = axi_wdata;
+ assign M_AXI_WSTRB[M*DW/8 +: DW/8] = axi_wstrb;
+ //
+ //
+ assign M_AXI_BREADY[M] = axi_bready;
+ //
+`ifdef FORMAL
+ // {{{
+ if (OPT_LOWPOWER)
+ begin
+ always @(*)
+ if (!axi_awvalid)
+ begin
+ assert(axi_awaddr == 0);
+ assert(axi_awprot == 0);
+ end
+
+ always @(*)
+ if (!axi_wvalid)
+ begin
+ assert(axi_wdata == 0);
+ assert(axi_wstrb == 0);
+ end
+ end
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : READ_SLAVE_OUTPUTS
+ // {{{
+ // Declarations
+ // {{{
+ reg axi_arvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+ reg [2:0] axi_arprot;
+ //
+ reg axi_rready;
+
+ wire arstall, srstall;
+ // }}}
+ assign arstall= (M_AXI_ARVALID[M]&& !M_AXI_ARREADY[M]);
+ assign srstall = (S_AXI_RVALID[mrindex[M]]
+ && !S_AXI_RREADY[mrindex[M]]);
+
+ // axi_arvalid
+ // {{{
+ initial axi_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !mrgrant[M])
+ axi_arvalid <= 0;
+ else if (!arstall)
+ begin
+ axi_arvalid <= m_arvalid[mrindex[M]] && slave_raccepts[mrindex[M]];
+ end
+ // }}}
+
+ // axi_araddr, axi_arprot
+ // {{{
+ initial axi_araddr = 0;
+ initial axi_arprot = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_araddr <= 0;
+ axi_arprot <= 0;
+ end else if (OPT_LOWPOWER && !mrgrant[M])
+ begin
+ axi_araddr <= 0;
+ axi_arprot <= 0;
+ end else if (!arstall)
+ begin
+ if (!OPT_LOWPOWER || (m_arvalid[mrindex[M]] && slave_raccepts[mrindex[M]]))
+ begin
+ if (NM == 1)
+ begin
+ axi_araddr <= m_araddr[0];
+ axi_arprot <= m_arprot[0];
+ end else begin
+ axi_araddr <= m_araddr[mrindex[M]];
+ axi_arprot <= m_arprot[mrindex[M]];
+ end
+ end else begin
+ axi_araddr <= 0;
+ axi_arprot <= 0;
+ end
+ end
+ // }}}
+
+ // axi_rready
+ // {{{
+ initial axi_rready = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !mrgrant[M])
+ axi_rready <= 1;
+ else if (!srstall)
+ axi_rready <= 1;
+ else if (M_AXI_RVALID[M] && M_AXI_RREADY[M]) // && srstall
+ axi_rready <= 0;
+ // }}}
+
+ //
+ assign M_AXI_ARVALID[M] = axi_arvalid;
+ assign M_AXI_ARADDR[M*AW +: AW] = axi_araddr;
+ assign M_AXI_ARPROT[M*3 +: 3] = axi_arprot;
+ //
+ assign M_AXI_RREADY[M] = axi_rready;
+ //
+`ifdef FORMAL
+ // {{{
+ if (OPT_LOWPOWER)
+ begin
+ always @(*)
+ if (!axi_arvalid)
+ begin
+ assert(axi_araddr == 0);
+ assert(axi_arprot == 0);
+ end
+ end
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+ // Return values
+ generate for (N=0; N<NM; N=N+1)
+ begin : WRITE_RETURN_CHANNEL
+ // {{{
+ reg axi_bvalid;
+ reg [1:0] axi_bresp;
+ reg i_axi_bvalid;
+ wire [1:0] i_axi_bresp;
+ wire mbstall;
+
+ initial i_axi_bvalid = 1'b0;
+ always @(*)
+ if (wgrant[N][NS])
+ i_axi_bvalid = m_wvalid[N] && slave_waccepts[N];
+ else
+ i_axi_bvalid = m_axi_bvalid[swindex[N]];
+
+ assign i_axi_bresp = m_axi_bresp[swindex[N]];
+
+ assign mbstall = S_AXI_BVALID[N] && !S_AXI_BREADY[N];
+
+ // r_bvalid
+ // {{{
+ initial r_bvalid[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_bvalid[N] <= 0;
+ else if (mbstall && !r_bvalid[N] && !wgrant[N][NS])
+ r_bvalid[N] <= swgrant[N] && i_axi_bvalid;
+ else if (!mbstall)
+ r_bvalid[N] <= 1'b0;
+ // }}}
+
+ // r_bresp
+ // {{{
+ initial r_bresp[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ r_bresp[N] <= 0;
+ else if (OPT_LOWPOWER && (!swgrant[N] || S_AXI_BREADY[N]))
+ r_bresp[N] <= 0;
+ else if (!r_bvalid[N])
+ begin
+ if (!OPT_LOWPOWER ||(i_axi_bvalid && !wgrant[N][NS] && mbstall))
+ begin
+ r_bresp[N] <= i_axi_bresp;
+ end else
+ r_bresp[N] <= 0;
+ end
+ // }}}
+
+ // axi_bvalid
+ // {{{
+ initial axi_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_bvalid <= 0;
+ else if (!mbstall)
+ axi_bvalid <= swgrant[N] && (r_bvalid[N] || i_axi_bvalid);
+ // }}}
+
+ // axi_bresp
+ // {{{
+ initial axi_bresp = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axi_bresp <= 0;
+ else if (OPT_LOWPOWER && !swgrant[N])
+ axi_bresp <= 0;
+ else if (!mbstall)
+ begin
+ if (r_bvalid[N])
+ axi_bresp <= r_bresp[N];
+ else if (!OPT_LOWPOWER || i_axi_bvalid)
+ axi_bresp <= i_axi_bresp;
+ else
+ axi_bresp <= 0;
+
+ if (wgrant[N][NS] && (!OPT_LOWPOWER || i_axi_bvalid))
+ axi_bresp <= INTERCONNECT_ERROR;
+ end
+ // }}}
+
+ //
+ assign S_AXI_BVALID[N] = axi_bvalid;
+ assign S_AXI_BRESP[N*2 +: 2] = axi_bresp;
+`ifdef FORMAL
+ // {{{
+ always @(*)
+ if (r_bvalid[N])
+ assert(r_bresp[N] != 2'b01);
+ always @(*)
+ if (swgrant[N])
+ assert(m_axi_bready[swindex[N]] == !r_bvalid[N]);
+ else
+ assert(!r_bvalid[N]);
+ always @(*)
+ if (OPT_LOWPOWER && !r_bvalid[N])
+ assert(r_bresp[N] == 0);
+
+ always @(*)
+ if (OPT_LOWPOWER && !axi_bvalid)
+ assert(axi_bresp == 0);
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+ // m_axi_?r* values
+ // {{{
+ always @(*)
+ begin
+ m_axi_arvalid = 0;
+ m_axi_arready = 0;
+ m_axi_rvalid = 0;
+ m_axi_rready = 0;
+
+ m_axi_arvalid[NS-1:0] = M_AXI_ARVALID;
+ m_axi_arready[NS-1:0] = M_AXI_ARREADY;
+ m_axi_rvalid[NS-1:0] = M_AXI_RVALID;
+ m_axi_rready[NS-1:0] = M_AXI_RREADY;
+ end
+ // }}}
+
+ // Return values
+ generate for (N=0; N<NM; N=N+1)
+ begin : READ_RETURN_CHANNEL
+ // {{{
+ reg axi_rvalid;
+ reg [1:0] axi_rresp;
+ reg [DW-1:0] axi_rdata;
+ wire srstall;
+ reg i_axi_rvalid;
+
+ initial i_axi_rvalid = 1'b0;
+ always @(*)
+ if (rgrant[N][NS])
+ i_axi_rvalid = m_arvalid[N] && slave_raccepts[N];
+ else
+ i_axi_rvalid = m_axi_rvalid[srindex[N]];
+
+ assign srstall = S_AXI_RVALID[N] && !S_AXI_RREADY[N];
+
+ initial r_rvalid[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_rvalid[N] <= 0;
+ else if (srstall && !r_rvalid[N])
+ r_rvalid[N] <= srgrant[N] && !rgrant[N][NS]&&i_axi_rvalid;
+ else if (!srstall)
+ r_rvalid[N] <= 0;
+
+ initial r_rresp[N] = 0;
+ initial r_rdata[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ r_rresp[N] <= 0;
+ r_rdata[N] <= 0;
+ end else if (OPT_LOWPOWER && (!srgrant[N] || S_AXI_RREADY[N]))
+ begin
+ r_rresp[N] <= 0;
+ r_rdata[N] <= 0;
+ end else if (!r_rvalid[N])
+ begin
+ if (!OPT_LOWPOWER || (i_axi_rvalid && !rgrant[N][NS] && srstall))
+ begin
+ if (NS == 1)
+ begin
+ r_rresp[N] <= m_axi_rresp[0];
+ r_rdata[N] <= m_axi_rdata[0];
+ end else begin
+ r_rresp[N] <= m_axi_rresp[srindex[N]];
+ r_rdata[N] <= m_axi_rdata[srindex[N]];
+ end
+ end else begin
+ r_rresp[N] <= 0;
+ r_rdata[N] <= 0;
+ end
+ end
+
+ initial axi_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_rvalid <= 0;
+ else if (!srstall)
+ axi_rvalid <= srgrant[N] && (r_rvalid[N] || i_axi_rvalid);
+
+ initial axi_rresp = 0;
+ initial axi_rdata = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_rresp <= 0;
+ axi_rdata <= 0;
+ end else if (OPT_LOWPOWER && !srgrant[N])
+ begin
+ axi_rresp <= 0;
+ axi_rdata <= 0;
+ end else if (!srstall)
+ begin
+ if (r_rvalid[N])
+ begin
+ axi_rresp <= r_rresp[N];
+ axi_rdata <= r_rdata[N];
+ end else if (!OPT_LOWPOWER || i_axi_rvalid)
+ begin
+ if (NS == 1)
+ begin
+ axi_rresp <= m_axi_rresp[0];
+ axi_rdata <= m_axi_rdata[0];
+ end else begin
+ axi_rresp <= m_axi_rresp[srindex[N]];
+ axi_rdata <= m_axi_rdata[srindex[N]];
+ end
+
+ if (rgrant[N][NS])
+ axi_rresp <= INTERCONNECT_ERROR;
+ end else begin
+ axi_rresp <= 0;
+ axi_rdata <= 0;
+ end
+ end
+
+ assign S_AXI_RVALID[N] = axi_rvalid;
+ assign S_AXI_RRESP[N*2 +: 2] = axi_rresp;
+ assign S_AXI_RDATA[N*DW +: DW]= axi_rdata;
+`ifdef FORMAL
+ // {{{
+ always @(*)
+ if (r_rvalid[N])
+ assert(r_rresp[N] != 2'b01);
+ always @(*)
+ if (srgrant[N] && !rgrant[N][NS])
+ assert(m_axi_rready[srindex[N]] == !r_rvalid[N]);
+ else
+ assert(!r_rvalid[N]);
+ always @(*)
+ if (OPT_LOWPOWER && !r_rvalid[N])
+ begin
+ assert(r_rresp[N] == 0);
+ assert(r_rdata[N] == 0);
+ end
+
+ always @(*)
+ if (OPT_LOWPOWER && !axi_rvalid)
+ begin
+ assert(axi_rresp == 0);
+ assert(axi_rdata == 0);
+ end
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+ // Count pending transactions
+ generate for (N=0; N<NM; N=N+1)
+ begin : COUNT_PENDING
+ // {{{
+ reg [LGMAXBURST-1:0] awpending, rpending,
+ missing_wdata;
+ //reg rempty, awempty; // wempty;
+ reg r_wdata_expected;
+
+ initial awpending = 0;
+ initial swempty[N] = 1;
+ initial swfull[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ awpending <= 0;
+ swempty[N] <= 1;
+ swfull[N] <= 0;
+ end else case ({(m_awvalid[N] && slave_awaccepts[N]),
+ (S_AXI_BVALID[N] && S_AXI_BREADY[N])})
+ 2'b01: begin
+ awpending <= awpending - 1;
+ swempty[N] <= (awpending <= 1);
+ swfull[N] <= 0;
+ end
+ 2'b10: begin
+ awpending <= awpending + 1;
+ swempty[N] <= 0;
+ swfull[N] <= &awpending[LGMAXBURST-1:1];
+ end
+ default: begin end
+ endcase
+
+ initial missing_wdata = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ missing_wdata <= 0;
+ else begin
+ missing_wdata <= missing_wdata
+ +((m_awvalid[N] && slave_awaccepts[N])? 1:0)
+ -((m_wvalid[N] && slave_waccepts[N])? 1:0);
+ end
+
+ initial r_wdata_expected = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_wdata_expected <= 0;
+ else case({ m_awvalid[N] && slave_awaccepts[N],
+ m_wvalid[N] && slave_waccepts[N] })
+ 2'b10: r_wdata_expected <= 1;
+ 2'b01: r_wdata_expected <= (missing_wdata > 1);
+ default: begin end
+ endcase
+
+
+ initial rpending = 0;
+ initial srempty[N] = 1;
+ initial srfull[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rpending <= 0;
+ srempty[N]<= 1;
+ srfull[N] <= 0;
+ end else case ({(m_arvalid[N] && slave_raccepts[N]),
+ (S_AXI_RVALID[N] && S_AXI_RREADY[N])})
+ 2'b01: begin
+ rpending <= rpending - 1;
+ srempty[N] <= (rpending == 1);
+ srfull[N] <= 0;
+ end
+ 2'b10: begin
+ rpending <= rpending + 1;
+ srfull[N] <= &rpending[LGMAXBURST-1:1];
+ srempty[N] <= 0;
+ end
+ default: begin end
+ endcase
+
+ assign w_sawpending[N] = awpending;
+ assign w_srpending[N] = rpending;
+
+ assign wdata_expected[N] = r_wdata_expected;
+
+`ifdef FORMAL
+ // {{{
+ reg [LGMAXBURST-1:0] wpending;
+ reg [LGMAXBURST-1:0] f_missing_wdata;
+
+ initial wpending = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ wpending <= 0;
+ else case ({(m_wvalid[N] && slave_waccepts[N]),
+ (S_AXI_BVALID[N] && S_AXI_BREADY[N])})
+ 2'b01: wpending <= wpending - 1;
+ 2'b10: wpending <= wpending + 1;
+ default: begin end
+ endcase
+
+ assign w_swpending[N] = wpending;
+
+ always @(*)
+ assert(missing_wdata == awpending - wpending);
+ always @(*)
+ assert(r_wdata_expected == (missing_wdata > 0));
+ always @(*)
+ assert(awpending >= wpending);
+ // }}}
+`endif
+ // }}}
+ end endgenerate
+
+ // Property validation
+ // {{{
+ initial begin
+ if (NM == 0) begin
+ $display("At least one master must be defined");
+ $stop;
+ end
+
+ if (NS == 0) begin
+ $display("At least one slave must be defined");
+ $stop;
+ end
+ end
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used to verify this core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Local declarations
+ // {{{
+ localparam F_LGDEPTH = LGMAXBURST+1;
+ wire [F_LGDEPTH-1:0] fm_rd_outstanding [0:NM-1];
+ wire [F_LGDEPTH-1:0] fm_wr_outstanding [0:NM-1];
+ wire [F_LGDEPTH-1:0] fm_awr_outstanding [0:NM-1];
+
+ wire [F_LGDEPTH-1:0] fs_rd_outstanding [0:NS-1];
+ wire [F_LGDEPTH-1:0] fs_wr_outstanding [0:NS-1];
+ wire [F_LGDEPTH-1:0] fs_awr_outstanding [0:NS-1];
+
+ initial assert(NS >= 1);
+ initial assert(NM >= 1);
+ // }}}
+
+`ifdef VERIFIC
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Initial value checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`ifdef VERIFIC
+`define INITIAL_CHECK assume
+`else
+`define INITIAL_CHECK assert
+`endif // VERIFIC
+ always @(*)
+ if (!f_past_valid)
+ begin
+ `INITIAL_CHECK(!S_AXI_ARESETN);
+ `INITIAL_CHECK(S_AXI_BVALID == 0);
+ `INITIAL_CHECK(S_AXI_RVALID == 0);
+ `INITIAL_CHECK(swgrant == 0);
+ `INITIAL_CHECK(srgrant == 0);
+ `INITIAL_CHECK(swfull == 0);
+ `INITIAL_CHECK(srfull == 0);
+ `INITIAL_CHECK(&swempty);
+ `INITIAL_CHECK(&srempty);
+ for(iN=0; iN<NM; iN=iN+1)
+ begin
+ `INITIAL_CHECK(wgrant[iN] == 0);
+ assume(swindex[iN] == 0);
+
+ `INITIAL_CHECK(rgrant[iN] == 0);
+ assume(srindex[iN] == 0);
+
+ `INITIAL_CHECK(r_bvalid[iN] == 0);
+ `INITIAL_CHECK(r_rvalid[iN] == 0);
+ //
+ `INITIAL_CHECK(r_bresp[iN] == 0);
+ //
+ `INITIAL_CHECK(r_rresp[iN] == 0);
+ `INITIAL_CHECK(r_rdata[iN] == 0);
+ end
+
+ `INITIAL_CHECK(M_AXI_AWVALID == 0);
+ `INITIAL_CHECK(M_AXI_WVALID == 0);
+ `INITIAL_CHECK(M_AXI_RVALID == 0);
+ end
+`endif
+ // }}}
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : CHECK_MASTER_GRANTS
+ // {{{
+
+ ////////////////////////////////////////////////////////////////
+ // Write grant checks
+ // {{{
+ always @(*)
+ for(iM=0; iM<=NS; iM=iM+1)
+ begin
+ if (wgrant[N][iM])
+ begin
+ assert((wgrant[N] ^ (1<<iM))==0);
+ assert(swgrant[N]);
+ assert(swindex[N] == iM);
+ if (iM < NS)
+ begin
+ assert(mwgrant[iM]);
+ assert(mwindex[iM] == N);
+ end
+ end
+ end
+
+ always @(*)
+ if (swgrant[N])
+ assert(wgrant[N] != 0);
+
+ always @(*)
+ if (wrequest[N][NS])
+ assert(wrequest[N][NS-1:0] == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Read grant checking
+ // {{{
+ always @(*)
+ for(iM=0; iM<=NS; iM=iM+1)
+ begin
+ if (rgrant[N][iM])
+ begin
+ assert((rgrant[N] ^ (1<<iM))==0);
+ assert(srgrant[N]);
+ assert(srindex[N] == iM);
+ if (iM < NS)
+ begin
+ assert(mrgrant[iM]);
+ assert(mrindex[iM] == N);
+ end
+ end
+ end
+
+ always @(*)
+ if (srgrant[N])
+ assert(rgrant[N] != 0);
+
+ always @(*)
+ if (rrequest[N][NS])
+ assert(rrequest[N][NS-1:0] == 0);
+ // }}}
+ // }}}
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : CHECK_MASTERS
+ // {{{
+ faxil_slave #(
+ .C_AXI_DATA_WIDTH(DW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_ASSUME_RESET(1'b1),
+ .F_AXI_MAXWAIT(0),
+ .F_AXI_MAXDELAY(0),
+ .F_LGDEPTH(F_LGDEPTH))
+ mstri(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID[N]),
+ .i_axi_awready(S_AXI_AWREADY[N]),
+ .i_axi_awaddr(S_AXI_AWADDR[N*AW +: AW]),
+ .i_axi_awprot(S_AXI_AWPROT[N*3 +: 3]),
+ //
+ .i_axi_wvalid(S_AXI_WVALID[N]),
+ .i_axi_wready(S_AXI_WREADY[N]),
+ .i_axi_wdata( S_AXI_WDATA[N*DW +: DW]),
+ .i_axi_wstrb( S_AXI_WSTRB[N*DW/8 +: DW/8]),
+ //
+ .i_axi_bvalid(S_AXI_BVALID[N]),
+ .i_axi_bready(S_AXI_BREADY[N]),
+ .i_axi_bresp( S_AXI_BRESP[N*2 +: 2]),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID[N]),
+ .i_axi_arready(S_AXI_ARREADY[N]),
+ .i_axi_araddr( S_AXI_ARADDR[N*AW +: AW]),
+ .i_axi_arprot( S_AXI_ARPROT[N*3 +: 3]),
+ //
+ //
+ .i_axi_rvalid(S_AXI_RVALID[N]),
+ .i_axi_rready(S_AXI_RREADY[N]),
+ .i_axi_rdata( S_AXI_RDATA[N*DW +: DW]),
+ .i_axi_rresp( S_AXI_RRESP[N*2 +: 2]),
+ //
+ .f_axi_rd_outstanding( fm_rd_outstanding[N]),
+ .f_axi_wr_outstanding( fm_wr_outstanding[N]),
+ .f_axi_awr_outstanding(fm_awr_outstanding[N]));
+
+ //
+ // Check write counters
+ //
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_awr_outstanding[N] == { 1'b0, w_sawpending[N] }
+ +((OPT_BUFFER_DECODER & dcd_awvalid[N]) ? 1:0)
+ + (S_AXI_AWREADY[N] ? 0:1));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_wr_outstanding[N] == { 1'b0, w_swpending[N] }
+ + (S_AXI_WREADY[N] ? 0:1));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_awr_outstanding[N] >=
+ (S_AXI_AWREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER & dcd_awvalid[N]) ? 1:0)
+ + (S_AXI_BVALID[N] ? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_wr_outstanding[N] >=
+ (S_AXI_WREADY[N] ? 0:1)
+ + (S_AXI_BVALID[N]? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_wr_outstanding[N]-(S_AXI_WREADY[N] ? 0:1)
+ <= fm_awr_outstanding[N]-(S_AXI_AWREADY[N] ? 0:1));
+
+ always @(*)
+ if (S_AXI_ARESETN && wgrant[N][NS])
+ assert(fm_wr_outstanding[N] == (S_AXI_WREADY[N] ? 0:1)
+ + (S_AXI_BVALID[N] ? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN && !swgrant[N])
+ begin
+ assert(!S_AXI_BVALID[N]);
+
+ assert(fm_awr_outstanding[N]==(S_AXI_AWREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER & dcd_awvalid[N]) ? 1:0));
+ assert(fm_wr_outstanding[N] == (S_AXI_WREADY[N] ? 0:1));
+ assert(w_sawpending[N] == 0);
+ assert(w_swpending[N] == 0);
+ end
+
+
+ //
+ // Check read counters
+ //
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_rd_outstanding[N] >=
+ (S_AXI_ARREADY[N] ? 0:1)
+ +(S_AXI_RVALID[N] ? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN && (!srgrant[N] || rgrant[N][NS]))
+ assert(fm_rd_outstanding[N] ==
+ (S_AXI_ARREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0)
+ +(S_AXI_RVALID[N] ? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(fm_rd_outstanding[N] == { 1'b0, w_srpending[N] }
+ +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0)
+ + (S_AXI_ARREADY[N] ? 0:1));
+
+ always @(*)
+ if (S_AXI_ARESETN && rgrant[N][NS])
+ assert(fm_rd_outstanding[N] == (S_AXI_ARREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0)
+ +(S_AXI_RVALID[N] ? 1:0));
+
+ always @(*)
+ if (S_AXI_ARESETN && !srgrant[N])
+ begin
+ assert(!S_AXI_RVALID[N]);
+ assert(fm_rd_outstanding[N]== (S_AXI_ARREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER && dcd_arvalid[N])? 1:0));
+ assert(w_srpending[N] == 0);
+ end
+
+ //
+ // Check full/empty flags
+ //
+ localparam [LGMAXBURST-1:0] NEAR_THRESHOLD = -2;
+
+ always @(*)
+ begin
+ assert(swfull[N] == &w_sawpending[N]);
+ assert(swempty[N] == (w_sawpending[N] == 0));
+ end
+
+ always @(*)
+ begin
+ assert(srfull[N] == &w_srpending[N]);
+ assert(srempty[N] == (w_srpending[N] == 0));
+ end
+ // }}}
+ end endgenerate
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : CHECK_SLAVES
+ // {{{
+ faxil_master #(
+ .C_AXI_DATA_WIDTH(DW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_ASSUME_RESET(1'b1),
+ .F_AXI_MAXRSTALL(0),
+ .F_LGDEPTH(F_LGDEPTH))
+ slvi(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID[M]),
+ .i_axi_awready(M_AXI_AWREADY[M]),
+ .i_axi_awaddr(M_AXI_AWADDR[M*AW +: AW]),
+ .i_axi_awprot(M_AXI_AWPROT[M*3 +: 3]),
+ //
+ .i_axi_wvalid(M_AXI_WVALID[M]),
+ .i_axi_wready(M_AXI_WREADY[M]),
+ .i_axi_wdata( M_AXI_WDATA[M*DW +: DW]),
+ .i_axi_wstrb( M_AXI_WSTRB[M*DW/8 +: DW/8]),
+ //
+ .i_axi_bvalid(M_AXI_BVALID[M]),
+ .i_axi_bready(M_AXI_BREADY[M]),
+ .i_axi_bresp( M_AXI_BRESP[M*2 +: 2]),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID[M]),
+ .i_axi_arready(M_AXI_ARREADY[M]),
+ .i_axi_araddr( M_AXI_ARADDR[M*AW +: AW]),
+ .i_axi_arprot( M_AXI_ARPROT[M*3 +: 3]),
+ //
+ //
+ .i_axi_rvalid(M_AXI_RVALID[M]),
+ .i_axi_rready(M_AXI_RREADY[M]),
+ .i_axi_rdata( M_AXI_RDATA[M*DW +: DW]),
+ .i_axi_rresp( M_AXI_RRESP[M*2 +: 2]),
+ //
+ .f_axi_rd_outstanding( fs_rd_outstanding[M]),
+ .f_axi_wr_outstanding( fs_wr_outstanding[M]),
+ .f_axi_awr_outstanding(fs_awr_outstanding[M]));
+
+ always @(*)
+ assert(fs_wr_outstanding[M] + (M_AXI_WVALID[M] ? 1:0)
+ <= fs_awr_outstanding[M] + (M_AXI_AWVALID[M]? 1:0));
+
+ always @(*)
+ if (!mwgrant[M])
+ begin
+ assert(fs_awr_outstanding[M] == 0);
+ assert(fs_wr_outstanding[M] == 0);
+ end
+
+ always @(*)
+ if (!mrgrant[M])
+ assert(fs_rd_outstanding[M] == 0);
+
+ always @(*)
+ assert(fs_awr_outstanding[M] < { 1'b1, {(F_LGDEPTH-1){1'b0}} });
+ always @(*)
+ assert(fs_wr_outstanding[M] < { 1'b1, {(F_LGDEPTH-1){1'b0}} });
+ always @(*)
+ assert(fs_rd_outstanding[M] < { 1'b1, {(F_LGDEPTH-1){1'b0}} });
+
+ always @(*)
+ if (M_AXI_AWVALID[M])
+ assert(((M_AXI_AWADDR[M*AW +: AW]
+ ^ SLAVE_ADDR[M*AW +: AW])
+ & SLAVE_MASK[M*AW +: AW]) == 0);
+
+ always @(*)
+ if (M_AXI_ARVALID[M])
+ assert(((M_AXI_ARADDR[M*AW +: AW]
+ ^ SLAVE_ADDR[M*AW +: AW])
+ & SLAVE_MASK[M*AW +: AW]) == 0);
+ // }}}
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : CORRELATE_OUTSTANDING
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN && (swgrant[N] && (swindex[N] < NS)))
+ begin
+ assert((fm_awr_outstanding[N]
+ - (S_AXI_AWREADY[N] ? 0:1)
+ -((OPT_BUFFER_DECODER && dcd_awvalid[N]) ? 1:0)
+ - (S_AXI_BVALID[N] ? 1:0))
+ == (fs_awr_outstanding[swindex[N]]
+ + (m_axi_awvalid[swindex[N]] ? 1:0)
+ + (m_axi_bready[swindex[N]] ? 0:1)));
+
+ assert((fm_wr_outstanding[N]
+ - (S_AXI_WREADY[N] ? 0:1)
+ - (S_AXI_BVALID[N] ? 1:0))
+ == (fs_wr_outstanding[swindex[N]]
+ + (m_axi_wvalid[swindex[N]] ? 1:0)
+ + (m_axi_bready[swindex[N]] ? 0:1)));
+
+ end else if (S_AXI_ARESETN && (!swgrant[N] || (swindex[N]==NS)))
+ begin
+ if (!swgrant[N])
+ assert(fm_awr_outstanding[N] ==
+ (S_AXI_AWREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER && dcd_awvalid[N]) ? 1:0)
+ +(S_AXI_BVALID[N] ? 1:0));
+ else
+ assert(fm_awr_outstanding[N] >=
+ (S_AXI_AWREADY[N] ? 0:1)
+ +((OPT_BUFFER_DECODER && dcd_awvalid[N]) ? 1:0)
+ +(S_AXI_BVALID[N] ? 1:0));
+
+ assert(fm_wr_outstanding[N] ==
+ (S_AXI_WREADY[N] ? 0:1)
+ +(S_AXI_BVALID[N] ? 1:0));
+ end
+
+ always @(*)
+ if (srgrant[N] && (srindex[N] < NS))
+ begin
+ assert((fm_rd_outstanding[N]//17
+ - (S_AXI_ARREADY[N] ? 0:1)//1
+ -((OPT_BUFFER_DECODER && dcd_arvalid[N]) ? 1:0)
+ - (S_AXI_RVALID[N] ? 1:0))//0
+ == (fs_rd_outstanding[srindex[N]]//16
+ + (m_axi_arvalid[srindex[N]] ? 1:0)//0
+ + (m_axi_rready[srindex[N]] ? 0:1)));//0
+ end
+ // }}}
+ end endgenerate
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // Can every master reach every slave?
+ // Can things transition without dropping the request line(s)?
+ generate for(N=0; N<NM; N=N+1)
+ begin : COVER_CONNECTIVITY_FROM_MASTER
+ reg [3:0] cvr_w_returns, cvr_r_returns;
+ reg err_wr_return, err_rd_return;
+ reg [NS-1:0] cvr_w_every, cvr_r_every;
+ reg cvr_was_wevery, cvr_was_revery,
+ cvr_whsreturn, cvr_rhsreturn;
+
+ // cvr_w_returns is a speed check: Can we return one write
+ // acknowledgement per clock cycle?
+ initial cvr_w_returns = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_w_returns = 0;
+ else begin
+ cvr_w_returns <= { cvr_w_returns[2:0], 1'b0 };
+ if (S_AXI_BVALID[N] && S_AXI_BREADY[N] && !wgrant[N][NS])
+ cvr_w_returns[0] <= 1'b1;
+ end
+
+ initial cvr_whsreturn = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_whsreturn <= 0;
+ else
+ cvr_whsreturn <= cvr_whsreturn || (&cvr_w_returns);
+
+ // w_every is a connectivity test: Can we get a return from
+ // every slave?
+ initial cvr_w_every = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_w_every <= 0;
+ else if (!S_AXI_AWVALID[N])
+ cvr_w_every <= 0;
+ else begin
+ if (S_AXI_BVALID[N] && S_AXI_BREADY[N] && !wgrant[N][NS])
+ cvr_w_every[swindex[N]] <= 1'b1;
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_BVALID[N])
+ assert($stable(swindex[N]));
+
+ initial cvr_was_wevery = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_was_wevery <= 0;
+ else
+ cvr_was_wevery <= cvr_was_wevery || (&cvr_w_every);
+
+ // err_wr_return is a test to make certain we can return a
+ // bus error on the write channel.
+ initial err_wr_return = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ err_wr_return = 0;
+ else if (wgrant[N][NS] && S_AXI_BVALID[N]
+ && (S_AXI_BRESP[2*N+:2]==INTERCONNECT_ERROR))
+ err_wr_return = 1;
+
+`ifndef VERILATOR
+ always @(*)
+ cover(!swgrant[N] && cvr_whsreturn);
+ always @(*)
+ cover(!swgrant[N] && cvr_was_wevery);
+
+ always @(*)
+ cover(S_AXI_ARESETN && wrequest[N][NS]);
+ always @(*)
+ cover(S_AXI_ARESETN && wrequest[N][NS] && slave_awaccepts[N]);
+ always @(*)
+ cover(err_wr_return);
+ always @(*)
+ cover(!swgrant[N] && err_wr_return);
+`endif
+
+ always @(*)
+ if (S_AXI_BVALID[N])
+ assert(swgrant[N]);
+
+ // cvr_r_returns is a speed check: Can we return one read
+ // acknowledgment per clock cycle?
+ initial cvr_r_returns = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_r_returns = 0;
+ else begin
+ cvr_r_returns <= { cvr_r_returns[2:0], 1'b0 };
+ if (S_AXI_RVALID[N] && S_AXI_RREADY[N])
+ cvr_r_returns[0] <= 1'b1;
+ end
+
+ initial cvr_rhsreturn = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_rhsreturn <= 0;
+ else
+ cvr_rhsreturn <= cvr_rhsreturn || (&cvr_r_returns);
+
+
+ // r_every is a connectivity test: Can we get a read return from
+ // every slave?
+ initial cvr_r_every = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_r_every = 0;
+ else if (!S_AXI_ARVALID[N])
+ cvr_r_every = 0;
+ else begin
+ if (S_AXI_RVALID[N] && S_AXI_RREADY[N])
+ cvr_r_every[srindex[N]] <= 1'b1;
+ end
+
+ // cvr_was_revery is a return to idle check following the
+ // connectivity test. Since the connectivity test is cleared
+ // if there's ever a drop in the valid line, we need a separate
+ // wire to check that this master can return to idle again.
+ initial cvr_was_revery = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_was_revery <= 0;
+ else
+ cvr_was_revery <= cvr_was_revery || (&cvr_r_every);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_RVALID[N])
+ assert($stable(srindex[N]));
+
+ initial err_rd_return = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ err_rd_return = 0;
+ else if (rgrant[N][NS] && S_AXI_RVALID[N]
+ && (S_AXI_RRESP[2*N+:2]==INTERCONNECT_ERROR))
+ err_rd_return = 1;
+
+`ifndef VERILATOR
+ always @(*)
+ cover(!srgrant[N] && cvr_rhsreturn); // @26
+ always @(*)
+ cover(!srgrant[N] && cvr_was_revery); // @26
+
+ always @(*)
+ cover(S_AXI_ARVALID[N] && rrequest[N][NS]);
+ always @(*)
+ cover(rgrant[N][NS]);
+ always @(*)
+ cover(err_rd_return);
+ always @(*)
+ cover(!srgrant[N] && err_rd_return); //@!
+`endif
+
+ always @(*)
+ if (S_AXI_BVALID[N] && wgrant[N][NS])
+ assert(S_AXI_BRESP[2*N+:2]==INTERCONNECT_ERROR);
+ always @(*)
+ if (S_AXI_RVALID[N] && rgrant[N][NS])
+ assert(S_AXI_RRESP[2*N+:2]==INTERCONNECT_ERROR);
+ end endgenerate
+
+ reg cvr_multi_write_hit, cvr_multi_read_hit;
+
+ initial cvr_multi_write_hit = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_multi_write_hit <= 0;
+ else if (fm_awr_outstanding[0] > 2 && !wgrant[0][NS])
+ cvr_multi_write_hit <= 1;
+
+ initial cvr_multi_read_hit = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_multi_read_hit <= 0;
+ else if (fm_rd_outstanding[0] > 2 && !rgrant[0][NS])
+ cvr_multi_read_hit <= 1;
+
+ always @(*)
+ cover(cvr_multi_write_hit);
+
+ always @(*)
+ cover(cvr_multi_read_hit);
+
+ always @(*)
+ cover(S_AXI_ARESETN && cvr_multi_write_hit & mwgrant == 0 && M_AXI_BVALID == 0);
+
+ always @(*)
+ cover(S_AXI_ARESETN && cvr_multi_read_hit & mrgrant == 0 && M_AXI_RVALID == 0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Negation check
+ // {{{
+ // Pick a particular value. Assume the value doesn't show up on the
+ // input. Prove it doesn't show up on the output. This will check for
+ // ...
+ // 1. Stuck bits on the output channel
+ // 2. Cross-talk between channels
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [LGNM-1:0] f_const_source;
+ (* anyconst *) reg [AW-1:0] f_const_addr;
+ (* anyconst *) reg [AW-1:0] f_const_addr_n;
+ (* anyconst *) reg [DW-1:0] f_const_data_n;
+ (* anyconst *) reg [DW/8-1:0] f_const_strb_n;
+ (* anyconst *) reg [3-1:0] f_const_prot_n;
+ (* anyconst *) reg [2-1:0] f_const_resp_n;
+ reg [LGNS-1:0] f_const_slave;
+
+ always @(*)
+ assume(f_const_source < NM);
+ always @(*)
+ begin
+ f_const_slave = NS;
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ if (((f_const_addr ^ SLAVE_ADDR[iM*AW+:AW])
+ &SLAVE_MASK[iM*AW+:AW])==0)
+ f_const_slave = iM;
+ end
+
+ assume(f_const_slave < NS);
+ end
+
+ reg [AW-1:0] f_awaddr;
+ reg [AW-1:0] f_araddr;
+ always @(*)
+ f_awaddr = S_AXI_AWADDR[f_const_source * AW +: AW];
+ always @(*)
+ f_araddr = S_AXI_ARADDR[f_const_source * AW +: AW];
+
+ // The assumption check: assume our negated values are not found on
+ // the inputs
+ always @(*)
+ begin
+ if (S_AXI_AWVALID[f_const_source])
+ begin
+ assume(f_awaddr != f_const_addr_n);
+ assume(S_AXI_AWPROT[f_const_source*3+:3] != f_const_prot_n);
+ end
+ if (m_wvalid)
+ begin
+ assume(m_wdata[f_const_source] != f_const_data_n);
+ assume(m_wstrb[f_const_source] != f_const_strb_n);
+ end
+ if (S_AXI_ARVALID[f_const_source])
+ begin
+ assume(f_araddr != f_const_addr_n);
+ assume(S_AXI_ARPROT[f_const_source*3+:3] != f_const_prot_n);
+ end
+
+ if (M_AXI_BVALID[f_const_slave] && wgrant[f_const_source][f_const_slave])
+ begin
+ assume(m_axi_bresp[f_const_slave] != f_const_resp_n);
+ end
+
+ if (M_AXI_RVALID[f_const_slave] && rgrant[f_const_source][f_const_slave])
+ begin
+ assume(m_axi_rdata[f_const_slave] != f_const_data_n);
+ assume(m_axi_rresp[f_const_slave] != f_const_resp_n);
+ end
+ end
+
+ // Proof check: Prove these values are not found on our outputs
+ always @(*)
+ begin
+ if (skd_awvalid[f_const_source])
+ begin
+ assert(skd_awaddr[f_const_source] != f_const_addr_n);
+ assert(skd_awprot[f_const_source] != f_const_prot_n);
+ end
+ if (dcd_awvalid[f_const_source])
+ begin
+ assert(m_awaddr[f_const_source] != f_const_addr_n);
+ assert(m_awprot[f_const_source] != f_const_prot_n);
+ end
+ if (M_AXI_AWVALID[f_const_slave] && wgrant[f_const_source][f_const_slave])
+ begin
+ assert(M_AXI_AWADDR[f_const_slave*AW+:AW] != f_const_addr_n);
+ assert(M_AXI_AWPROT[f_const_slave*3+:3] != f_const_prot_n);
+ end
+ if (M_AXI_WVALID[f_const_slave] && wgrant[f_const_source][f_const_slave])
+ begin
+ assert(M_AXI_WDATA[f_const_slave*DW+:DW] != f_const_data_n);
+ assert(M_AXI_WSTRB[f_const_slave*(DW/8)+:(DW/8)] != f_const_strb_n);
+ end
+ if (skd_arvalid[f_const_source])
+ begin
+ assert(skd_araddr[f_const_source] != f_const_addr_n);
+ assert(skd_arprot[f_const_source] != f_const_prot_n);
+ end
+ if (dcd_arvalid[f_const_source])
+ begin
+ assert(m_araddr[f_const_source] != f_const_addr_n);
+ assert(m_arprot[f_const_source] != f_const_prot_n);
+ end
+ if (M_AXI_ARVALID[f_const_slave] && rgrant[f_const_source][f_const_slave])
+ begin
+ assert(M_AXI_ARADDR[f_const_slave*AW+:AW] != f_const_addr_n);
+ assert(M_AXI_ARPROT[f_const_slave*3+:3] != f_const_prot_n);
+ end
+ //
+ if (r_bvalid[f_const_source] && wgrant[f_const_source][f_const_slave])
+ assert(r_bresp[f_const_source] != f_const_resp_n);
+ if (S_AXI_BVALID[f_const_source] && wgrant[f_const_source][f_const_slave])
+ assert(S_AXI_BRESP[f_const_source*2+:2] != f_const_resp_n);
+ if (r_rvalid[f_const_source] && rgrant[f_const_source][f_const_slave])
+ begin
+ assert(r_rresp[f_const_source] != f_const_resp_n);
+ assert(r_rdata[f_const_source] != f_const_data_n);
+ end
+ if (S_AXI_RVALID[f_const_source] && rgrant[f_const_source][f_const_slave])
+ begin
+ assert(S_AXI_RRESP[f_const_source*2+:2]!=f_const_resp_n);
+ assert(S_AXI_RDATA[f_const_source*DW+:DW]!=f_const_data_n);
+ end
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Careless) constraining assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin
+
+ end endgenerate
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axim2wbsp.v b/rtl/wb2axip/axim2wbsp.v
new file mode 100644
index 0000000..fb58458
--- /dev/null
+++ b/rtl/wb2axip/axim2wbsp.v
@@ -0,0 +1,317 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axim2wbsp.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: So ... this converter works in the other direction from
+// wbm2axisp. This converter takes AXI commands, and organizes
+// them into pipelined wishbone commands.
+//
+// This particular core treats AXI as two separate buses: one for writes,
+// and the other for reads. This particular core combines the two channels
+// into one. The designer should be aware that the two AXI buses turned
+// Wishbone buses can be kept separate as separate inputs to a WB crosssbar
+// for better performance in some circumstances.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axim2wbsp #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 2, // The AXI id width used for R&W
+ // This is an int between 1-16
+ parameter C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
+ parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width
+ localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam DW = C_AXI_DATA_WIDTH,
+ localparam AW = C_AXI_ADDR_WIDTH - AXI_LSBS,
+ parameter LGFIFO = 5,
+ parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
+ parameter [0:0] OPT_READONLY = 1'b0,
+ parameter [0:0] OPT_WRITEONLY = 1'b0
+ // }}}
+ ) (
+ // {{{
+ //
+ input wire S_AXI_ACLK, // System clock
+ input wire S_AXI_ARESETN,
+
+ // AXI write address channel signals
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [7:0] S_AXI_AWLEN,
+ input wire [2:0] S_AXI_AWSIZE,
+ input wire [1:0] S_AXI_AWBURST,
+ input wire [0:0] S_AXI_AWLOCK,
+ input wire [3:0] S_AXI_AWCACHE,
+ input wire [2:0] S_AXI_AWPROT,
+ input wire [3:0] S_AXI_AWQOS,
+ // }}}
+ // AXI write data channel signals
+ // {{{
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ // }}}
+ // AXI write response channel signals
+ // {{{
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ // }}}
+ // AXI read address channel signals
+ // {{{
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [7:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire [0:0] S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+ // }}}
+ // AXI read data channel signals
+ // {{{
+ output wire S_AXI_RVALID, // Rd rslt valid
+ input wire S_AXI_RREADY, // Rd rslt ready
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID, // Response ID
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,// Read data
+ output wire S_AXI_RLAST, // Read last
+ output wire [1:0] S_AXI_RRESP, // Read response
+ // }}}
+ // We'll share the clock and the reset
+ // {{{
+ output wire o_reset,
+ output wire o_wb_cyc,
+ output wire o_wb_stb,
+ output wire o_wb_we,
+ output wire [(AW-1):0] o_wb_addr,
+ output wire [(C_AXI_DATA_WIDTH-1):0] o_wb_data,
+ output wire [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
+ input wire i_wb_stall,
+ input wire i_wb_ack,
+ input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
+ input wire i_wb_err
+ // }}}
+ // }}}
+ );
+ //
+ //
+ //
+
+
+ wire [(AW-1):0] w_wb_addr, r_wb_addr;
+ wire [(C_AXI_DATA_WIDTH-1):0] w_wb_data;
+ wire [(C_AXI_DATA_WIDTH/8-1):0] w_wb_sel, r_wb_sel;
+ wire r_wb_err, r_wb_cyc, r_wb_stb, r_wb_stall, r_wb_ack;
+ wire w_wb_err, w_wb_cyc, w_wb_stb, w_wb_stall, w_wb_ack;
+ wire r_wb_we, w_wb_we;
+
+ assign r_wb_we = 1'b0;
+ assign w_wb_we = 1'b1;
+
+ generate if (!OPT_READONLY)
+ begin : AXI_WR
+ // {{{
+ aximwr2wbsp #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .OPT_SWAP_ENDIANNESS(OPT_SWAP_ENDIANNESS),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axi_write_decoder(
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK), .S_AXI_ARESETN(S_AXI_ARESETN),
+ //
+ .S_AXI_AWVALID(S_AXI_AWVALID),
+ .S_AXI_AWREADY(S_AXI_AWREADY),
+ .S_AXI_AWID( S_AXI_AWID),
+ .S_AXI_AWADDR( S_AXI_AWADDR),
+ .S_AXI_AWLEN( S_AXI_AWLEN),
+ .S_AXI_AWSIZE( S_AXI_AWSIZE),
+ .S_AXI_AWBURST(S_AXI_AWBURST),
+ .S_AXI_AWLOCK( S_AXI_AWLOCK),
+ .S_AXI_AWCACHE(S_AXI_AWCACHE),
+ .S_AXI_AWPROT( S_AXI_AWPROT),
+ .S_AXI_AWQOS( S_AXI_AWQOS),
+ //
+ .S_AXI_WVALID( S_AXI_WVALID),
+ .S_AXI_WREADY( S_AXI_WREADY),
+ .S_AXI_WDATA( S_AXI_WDATA),
+ .S_AXI_WSTRB( S_AXI_WSTRB),
+ .S_AXI_WLAST( S_AXI_WLAST),
+ //
+ .S_AXI_BVALID(S_AXI_BVALID),
+ .S_AXI_BREADY(S_AXI_BREADY),
+ .S_AXI_BID( S_AXI_BID),
+ .S_AXI_BRESP( S_AXI_BRESP),
+ //
+ .o_wb_cyc( w_wb_cyc),
+ .o_wb_stb( w_wb_stb),
+ .o_wb_addr( w_wb_addr),
+ .o_wb_data( w_wb_data),
+ .o_wb_sel( w_wb_sel),
+ .i_wb_ack( w_wb_ack),
+ .i_wb_stall(w_wb_stall),
+ .i_wb_err( w_wb_err)
+ // }}}
+ );
+ // }}}
+ end else begin : NO_WRITE_CHANNEL
+ // {{{
+ assign w_wb_cyc = 0;
+ assign w_wb_stb = 0;
+ assign w_wb_addr = 0;
+ assign w_wb_data = 0;
+ assign w_wb_sel = 0;
+ assign S_AXI_AWREADY = 0;
+ assign S_AXI_WREADY = 0;
+ assign S_AXI_BVALID = 0;
+ assign S_AXI_BRESP = 2'b11;
+ assign S_AXI_BID = 0;
+ // }}}
+ end endgenerate
+
+ generate if (!OPT_WRITEONLY)
+ begin : AXI_RD
+ // {{{
+ aximrd2wbsp #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .OPT_SWAP_ENDIANNESS(OPT_SWAP_ENDIANNESS),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axi_read_decoder(
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK), .S_AXI_ARESETN(S_AXI_ARESETN),
+ //
+ .S_AXI_ARVALID(S_AXI_ARVALID),
+ .S_AXI_ARREADY(S_AXI_ARREADY),
+ .S_AXI_ARID( S_AXI_ARID),
+ .S_AXI_ARADDR( S_AXI_ARADDR),
+ .S_AXI_ARLEN( S_AXI_ARLEN),
+ .S_AXI_ARSIZE( S_AXI_ARSIZE),
+ .S_AXI_ARBURST(S_AXI_ARBURST),
+ .S_AXI_ARLOCK( S_AXI_ARLOCK),
+ .S_AXI_ARCACHE(S_AXI_ARCACHE),
+ .S_AXI_ARPROT( S_AXI_ARPROT),
+ .S_AXI_ARQOS( S_AXI_ARQOS),
+ //
+ .S_AXI_RVALID(S_AXI_RVALID),
+ .S_AXI_RREADY(S_AXI_RREADY),
+ .S_AXI_RID( S_AXI_RID),
+ .S_AXI_RDATA( S_AXI_RDATA),
+ .S_AXI_RLAST( S_AXI_RLAST),
+ .S_AXI_RRESP( S_AXI_RRESP),
+ //
+ .o_wb_cyc( r_wb_cyc),
+ .o_wb_stb( r_wb_stb),
+ .o_wb_addr( r_wb_addr),
+ .o_wb_sel( r_wb_sel),
+ .i_wb_ack( r_wb_ack),
+ .i_wb_stall(r_wb_stall),
+ .i_wb_data( i_wb_data),
+ .i_wb_err( r_wb_err)
+ // }}}
+ );
+ // }}}
+ end else begin : NO_READ_CHANNEL
+ // {{{
+ assign r_wb_cyc = 0;
+ assign r_wb_stb = 0;
+ assign r_wb_addr = 0;
+ //
+ assign S_AXI_ARREADY = 0;
+ assign S_AXI_RVALID = 0;
+ assign S_AXI_RID = 0;
+ assign S_AXI_RDATA = 0;
+ assign S_AXI_RLAST = 0;
+ assign S_AXI_RRESP = 0;
+ // }}}
+ end endgenerate
+
+ generate if (OPT_READONLY)
+ begin : ARB_RD
+ // {{{
+ assign o_wb_cyc = r_wb_cyc;
+ assign o_wb_stb = r_wb_stb;
+ assign o_wb_we = r_wb_we;
+ assign o_wb_addr = r_wb_addr;
+ assign o_wb_data = 0;
+ assign o_wb_sel = r_wb_sel;
+ assign r_wb_ack = i_wb_ack;
+ assign r_wb_stall= i_wb_stall;
+ assign r_wb_ack = i_wb_ack;
+ assign r_wb_err = i_wb_err;
+ // }}}
+ end else if (OPT_WRITEONLY)
+ begin : ARB_WR
+ // {{{
+ assign o_wb_cyc = w_wb_cyc;
+ assign o_wb_stb = w_wb_stb;
+ assign o_wb_we = w_wb_we;
+ assign o_wb_addr = w_wb_addr;
+ assign o_wb_data = w_wb_data;
+ assign o_wb_sel = w_wb_sel;
+ assign w_wb_ack = i_wb_ack;
+ assign w_wb_stall= i_wb_stall;
+ assign w_wb_ack = i_wb_ack;
+ assign w_wb_err = i_wb_err;
+ // }}}
+ end else begin : ARB_WB
+ // {{{
+ wbarbiter #(.DW(DW), .AW(AW))
+ readorwrite(S_AXI_ACLK, o_reset,
+ r_wb_cyc, r_wb_stb, r_wb_we, r_wb_addr, w_wb_data, r_wb_sel,
+ r_wb_ack, r_wb_stall, r_wb_err,
+ w_wb_cyc, w_wb_stb, w_wb_we, w_wb_addr, w_wb_data, w_wb_sel,
+ w_wb_ack, w_wb_stall, w_wb_err,
+ o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
+ i_wb_ack, i_wb_stall, i_wb_err
+ );
+ // }}}
+ end endgenerate
+
+ assign o_reset = (S_AXI_ARESETN == 1'b0);
+
+`ifdef FORMAL
+`endif
+endmodule
diff --git a/rtl/wb2axip/aximm2s.v b/rtl/wb2axip/aximm2s.v
new file mode 100644
index 0000000..b4c1056
--- /dev/null
+++ b/rtl/wb2axip/aximm2s.v
@@ -0,0 +1,2158 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: aximm2s
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Converts an AXI (full) memory port to an AXI-stream
+// interface.
+// // {{{
+// While I am aware that other vendors sell similar components, if you
+// look under the hood you'll find no relation to anything but my own
+// work here.
+//
+// Registers:
+//
+// 0: CMD_CONTROL
+// Controls the transaction via either starting or aborting an
+// ongoing transaction, provides feedback regarding the current
+// status.
+//
+// [31] r_busy
+// True if the core is in the middle of a transaction
+//
+// [30] r_err
+// True if the core has detected an error, a bus error
+// while the FIFO is reading.
+//
+// Writing a '1' to this bit while the core is idle will clear it.
+// New transfers will not start until this bit is cleared.
+//
+// [29] r_complete
+// True if the transaction has completed, whether normally or
+// abnormally (error or abort).
+//
+// Any write to the CMD_CONTROL register will clear this flag.
+//
+// [28] r_continuous
+// Normally the FIFO gets cleared and reset between operations.
+// However, if you set r_continuous, the core will then expect
+// a second operation to take place following the first one.
+// In this case, the operation will complete but the FIFO won't
+// get cleared. During this time, the FIFO will not fill further.
+//
+// Any write to the CMD_CONTROL register while the core is not
+// busy will adjust this bit.
+//
+// [27] !r_increment
+//
+// If clear, the core reads from subsequent and incrementing
+// addresses. If set, the core reads from the same address
+// throughout a transaction.
+//
+// Writes to CMD_CONTROL while the core is idle will adjust this
+// bit.
+//
+// [20:16] LGFIFO
+// These are read-only bits, returning the size of the FIFO.
+//
+// ABORT
+// If the core is busy, and ABORT_KEY (currently set to 8'h6d
+// below) is written to the top 8-bits of this register,
+// the current transfer will be aborted. Any pending reads
+// will be completed, but nothing more will be written to the
+// stream.
+//
+// Alternatively, the core will enter into an abort state
+// following any returned bus error indications.
+//
+// 4: (Unused and reserved)
+//
+// 8-c: CMD_ADDRLO, CMD_ADDR_HI
+// [C_AXI_ADDR_WIDTH-1:($clog2(C_AXI_DATA_WIDTH)-3)]
+// If idle, the address the core will read from when it starts.
+// If busy, the address the core is currently reading from.
+// Upon completion, the address either returns to the starting
+// address (if r_continuous is clear), or otherwise becomes the
+// address where the core left off. In the case of an abort or an
+// error, this will be (near) the address that was last read.
+//
+// Why "near"? Because this address records the reads that have
+// been issued while no error is pending. If a bus error return
+// comes back, there may have been several more reads issued before
+// that error address.
+//
+// 10-14: (Unused and reserved)
+//
+// 18-1c: CMD_LENLO, CMD_LENHI
+// [LGLEN-1:0]
+// The size of the transfer in bytes. Only accepts aligned
+// addresses, therefore bits [($clog2(C_AXI_DATA_WIDTH)-3):0]
+// will always be forced to zero. To find out what size bus
+// this core is conencted to, or the maximum transfer length,
+// write a -1 to this value and read the returning result.
+// Only the active bits will be set.
+//
+// While the core is busy, reads from this address will return
+// the number of items still to be read from the bus.
+//
+// I hope to eventually add support for unaligned bursts. Such
+// support is not currently part of this core.
+//
+// }}}
+//
+// Status:
+// {{{
+// 1. The core passes both cover checks and formal property (assertion)
+// based checks. It has not (yet) been tested in real hardware.
+//
+// 2. I'd like to support unaligned addresses and lengths. This will
+// require aligning the data coming out of the FIFO as well.
+// As written, the core doesn't yet support these.
+//
+// }}}
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module aximm2s #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 1,
+ //
+ // We support five 32-bit AXI-lite registers, requiring 5-bits
+ // of AXI-lite addressing
+ localparam C_AXIL_ADDR_WIDTH = 5,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ //
+ // The bottom ADDRLSB bits of any AXI address are subword bits
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3,
+ //
+ // OPT_UNALIGNED: Allow unaligned accesses, address requests
+ // and sizes which may or may not match the underlying data
+ // width. If set, the core will quietly align these requests.
+ parameter [0:0] OPT_UNALIGNED = 1'b0,
+ //
+ // OPT_TKEEP [Future]: If set, will also add TKEEP signals to
+ // the outgoing slave interface. This is necessary if ever you
+ // wish to output partial stream words, such as might happen if
+ // the length were ever something other than a full number of
+ // words. (Not yet implemented)
+ // parameter [0:0] OPT_TKEEP = 1'b0,
+ //
+ // OPT_TLAST: If enabled, will embed TLAST=1 at the end of every
+ // commanded burst. If disabled, TLAST will be set to a
+ // constant 1'b1.
+ parameter [0:0] OPT_TLAST = 1'b0,
+ //
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ parameter [0:0] OPT_CLKGATE = OPT_LOWPOWER,
+ //
+ // ABORT_KEY is the value that, when written to the top 8-bits
+ // of the control word, will abort any ongoing operation.
+ parameter [7:0] ABORT_KEY = 8'h6d,
+ //
+ // The size of the FIFO
+ parameter LGFIFO = 9,
+ //
+ // Maximum number of bytes that can ever be transferred, in
+ // log-base 2
+ parameter LGLEN = 20,
+ //
+ // AXI_ID is the ID we will use for all of our AXI transactions
+ parameter AXI_ID = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The stream interface
+ // {{{
+ output wire M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output wire M_AXIS_TLAST,
+ // }}}
+ //
+ // The control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ //
+
+ //
+ // The AXI (full) read interface
+ // {{{
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [7:0] M_AXI_ARLEN,
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [1:0] M_AXI_RRESP,
+ // }}}
+ //
+ //
+ // Create an output signal to indicate that we've finished
+ output reg o_int
+ // }}}
+ );
+
+ // Local parameter declarations
+ // {{{
+ localparam [2:0] CMD_CONTROL = 3'b000,
+ // CMD_UNUSED_1 = 3'b001,
+ CMD_ADDRLO = 3'b010,
+ CMD_ADDRHI = 3'b011,
+ // CMD_UNUSED_2 = 3'b100,
+ // CMD_UNUSED_3 = 3'b101,
+ CMD_LENLO = 3'b110,
+ CMD_LENHI = 3'b111;
+ localparam CBIT_BUSY = 31,
+ CBIT_ERR = 30,
+ CBIT_COMPLETE = 29,
+ CBIT_CONTINUOUS = 28,
+ CBIT_INCREMENT = 27;
+ localparam TMP_LGMAXBURST=(LGFIFO > 8) ? 8 : LGFIFO-1;
+ localparam LGMAXBURST = ((4096/(C_AXI_DATA_WIDTH/8))
+ > (1<<TMP_LGMAXBURST))
+ ? TMP_LGMAXBURST : $clog2(4096 * 8 / C_AXI_DATA_WIDTH);
+ localparam LGMAX_FIXED_BURST = (LGMAXBURST < 4) ? LGMAXBURST : 4,
+ MAX_FIXED_BURST = (1<<LGMAX_FIXED_BURST);
+ localparam LGLENW = LGLEN - ($clog2(C_AXI_DATA_WIDTH)-3),
+ LGLENWA = LGLENW + (OPT_UNALIGNED ? 1:0);
+ // localparam LGFIFOB = LGFIFO + ($clog2(C_AXI_DATA_WIDTH)-3);
+ // localparam [ADDRLSB-1:0] LSBZEROS = 0;
+ // }}}
+
+ // Signal declarations
+ // {{{
+ wire clk_active, gated_clk;
+ wire i_clk = gated_clk;
+ wire i_reset = !S_AXI_ARESETN;
+
+ reg r_busy, r_err, r_complete, r_continuous, r_increment,
+ cmd_abort, zero_length,
+ w_cmd_start, w_complete, w_cmd_abort, r_pre_start;
+ // reg cmd_start;
+ reg axi_abort_pending;
+
+ reg [LGLENWA-1:0] ar_requests_remaining,
+ ar_bursts_outstanding,
+ ar_next_remaining;
+ reg [LGMAXBURST:0] r_max_burst;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_raddr;
+
+ reg [C_AXI_ADDR_WIDTH-1:0] cmd_addr;
+ reg [LGLENW-1:0] cmd_length_w;
+ reg [LGLENWA-1:0] cmd_length_aligned_w;
+ reg unaligned_cmd_addr;
+
+ // FIFO signals
+ wire reset_fifo, write_to_fifo,
+ read_from_fifo;
+ wire [C_AXI_DATA_WIDTH-1:0] write_data;
+ wire [LGFIFO:0] fifo_fill;
+ wire fifo_full, fifo_empty;
+
+ wire awskd_valid, axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ //
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire arskd_valid, axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+ reg [C_AXIL_DATA_WIDTH-1:0] w_status_word;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, wide_length,
+ new_wideaddr, new_widelen;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi,
+ new_lengthlo, new_lengthhi;
+
+
+
+ reg axi_arvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+ reg [7:0] axi_arlen;
+
+ // Speed up checking for zeros
+ reg ar_none_remaining,
+ ar_none_outstanding,
+ phantom_start, start_burst;
+ reg ar_multiple_full_bursts,
+ ar_multiple_fixed_bursts,
+ ar_multiple_bursts_remaining,
+ ar_needs_alignment;
+ wire partial_burst_requested;
+ reg [LGMAXBURST-1:0] addralign;
+ reg [LGFIFO:0] rd_uncommitted;
+ reg [LGMAXBURST:0] initial_burstlen;
+ reg [LGLENWA-1:0] rd_reads_remaining;
+ reg rd_none_remaining,
+ rd_last_remaining;
+
+ wire realign_last_valid;
+/*
+ wr_none_pending, r_none_remaining;
+
+ reg w_phantom_start, phantom_start;
+ reg [LGFIFO:0] next_fill;
+*/
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // This is mostly the skidbuffer logic, and handling of the VALID
+ // and READY signals for the AXI-lite control logic in the next
+ // section.
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_ADDR_WIDTH-AXILLSB)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8)
+ // }}}
+ ) axilwskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ assign axil_write_ready = clk_active && awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_ADDR_WIDTH-AXILLSB)
+ // }}}
+ ) axilarskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr)
+ // }}}
+ );
+
+ assign axil_read_ready = clk_active && arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite controlled logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // w_cmd_abort, cmd_abort : Abort transaction
+ // {{{
+ always @(*)
+ begin
+ w_cmd_abort = 0;
+ w_cmd_abort = (axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[31:24] == ABORT_KEY);
+ if (!r_busy)
+ w_cmd_abort = 0;
+ end
+
+ initial cmd_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cmd_abort <= 0;
+ else
+ cmd_abort <= (cmd_abort && r_busy)||w_cmd_abort;
+ // }}}
+
+ // w_cmd_start: Start command
+ // {{{
+ always @(*)
+ if (r_busy)
+ w_cmd_start = 0;
+ else begin
+ w_cmd_start = 0;
+ if ((axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[CBIT_BUSY]))
+ w_cmd_start = 1;
+ if (r_err && !wskd_data[CBIT_ERR])
+ w_cmd_start = 0;
+ if (zero_length)
+ w_cmd_start = 0;
+ if (OPT_UNALIGNED && unaligned_cmd_addr
+ && wskd_data[CBIT_INCREMENT])
+ w_cmd_start = 0;
+ end
+ // }}}
+
+ // r_busy, r_complete: Calculate busy or complete flags
+ // {{{
+ initial r_busy = 0;
+ initial r_complete = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_busy <= 0;
+ r_complete <= 0;
+ end else if (!r_busy)
+ begin
+ if (w_cmd_start)
+ r_busy <= 1'b1;
+ if (axil_write_ready && awskd_addr == CMD_CONTROL)
+ // Any write to the control register will clear the
+ // completion flag
+ r_complete <= 1'b0;
+ end else if (r_busy)
+ begin
+ if (w_complete)
+ begin
+ r_complete <= 1;
+ r_busy <= 1'b0;
+ end
+ end
+ // }}}
+
+ // o_int: Interrupts
+ // {{{
+ initial o_int = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_int <= 0;
+ else
+ o_int <= (r_busy && w_complete);
+ // }}}
+
+ // r_err : Error conditions
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset)
+ r_err <= 0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready && awskd_addr == CMD_CONTROL)
+ begin
+ if (!w_cmd_abort)
+ r_err <= r_err && (!wskd_strb[3] || !wskd_data[CBIT_ERR]);
+ // On any request to start a transfer with an unaligned
+ // address, that's not incrementing--declare an
+ // immediate error
+ if (wskd_strb[3] && wskd_data[CBIT_BUSY]
+ && wskd_data[CBIT_INCREMENT]
+ && (OPT_UNALIGNED && unaligned_cmd_addr))
+ r_err <= 1'b1;
+ end
+ end else // if (r_busy)
+ begin
+ if (M_AXI_RVALID && M_AXI_RREADY && M_AXI_RRESP[1])
+ r_err <= 1'b1;
+ end
+ // }}}
+
+ // r_continuous
+ // {{{
+ initial r_continuous = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_continuous <= 0;
+ else begin
+ if (!r_busy && axil_write_ready && awskd_addr == CMD_CONTROL
+ && !w_cmd_abort)
+ r_continuous <= wskd_strb[3] && wskd_data[CBIT_CONTINUOUS];
+ end
+ // }}}
+
+ // wide_address, wide_length
+ // {{{
+ always @(*)
+ begin
+ wide_address = 0;
+ wide_address[C_AXI_ADDR_WIDTH-1:0] = cmd_addr;
+ if (!OPT_UNALIGNED)
+ wide_address[ADDRLSB-1:0] = 0;
+
+ wide_length = 0;
+ wide_length[ADDRLSB +: LGLENW] = cmd_length_w;
+ end
+ // }}}
+
+ // new_cmdaddr*
+ // {{{
+ assign new_cmdaddrlo = apply_wstrb(
+ wide_address[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+
+ assign new_cmdaddrhi = apply_wstrb(
+ wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ // }}}
+
+ // new_length*
+ // {{{
+ assign new_lengthlo = apply_wstrb(
+ wide_length[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+
+ assign new_lengthhi = apply_wstrb(
+ wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ // }}}
+
+ always @(*)
+ begin
+ new_wideaddr = wide_address;
+
+ if (awskd_addr == CMD_ADDRLO)
+ new_wideaddr[C_AXIL_DATA_WIDTH-1:0] = new_cmdaddrlo;
+ if (awskd_addr == CMD_ADDRHI)
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH] = new_cmdaddrhi;
+ if (!OPT_UNALIGNED)
+ new_wideaddr[ADDRLSB-1:0] = 0;
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+
+
+ new_widelen = wide_length;
+ if (awskd_addr == CMD_LENLO)
+ new_widelen[C_AXIL_DATA_WIDTH-1:0] = new_lengthlo;
+ if (awskd_addr == CMD_LENHI)
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH] = new_lengthhi;
+ new_widelen[ADDRLSB-1:0] = 0;
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:ADDRLSB+LGLENW] = 0;
+ end
+
+
+ initial r_increment = 1'b1;
+ initial cmd_addr = 0;
+ initial cmd_length_w = 0; // Counts in bytes
+ initial zero_length = 1;
+ initial cmd_length_aligned_w = 0;
+ initial unaligned_cmd_addr = 1'b0;
+ initial ar_multiple_full_bursts = 0;
+ initial ar_multiple_fixed_bursts = 0;
+ always @(posedge i_clk)
+ begin
+ if (axil_write_ready && !r_busy)
+ begin
+ case(awskd_addr)
+ CMD_CONTROL:
+ r_increment <= !wskd_data[CBIT_INCREMENT];
+ CMD_ADDRLO: begin
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ unaligned_cmd_addr <= |new_wideaddr[ADDRLSB-1:0];
+ if (OPT_UNALIGNED)
+ cmd_length_aligned_w <= cmd_length_w
+ + (|new_wideaddr[ADDRLSB-1:0] ? 1:0);
+ // ERR: What if !r_increment? In that case, we can't
+ // support unaligned addressing
+ end
+ CMD_ADDRHI: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH)
+ begin
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ end
+ CMD_LENLO: begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ cmd_length_aligned_w <= new_widelen[ADDRLSB +: LGLENW]
+ + (unaligned_cmd_addr ? 1:0);
+ ar_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ ar_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+ end
+ CMD_LENHI: begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ cmd_length_aligned_w <= new_widelen[ADDRLSB +: LGLENW]
+ + (unaligned_cmd_addr ? 1:0);
+ ar_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ ar_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+ end
+ default: begin end
+ endcase
+ end else if(r_busy && r_continuous && !axi_abort_pending
+ && r_increment)
+ cmd_addr <= axi_raddr
+ + ((M_AXI_RVALID && !M_AXI_RRESP[1]
+ && (!unaligned_cmd_addr || realign_last_valid))
+ ? (1<<ADDRLSB) : 0);
+
+ if (i_reset)
+ begin
+ r_increment <= 1'b1;
+ cmd_addr <= 0;
+ cmd_length_w <= 0;
+ zero_length <= 1;
+ unaligned_cmd_addr <= 0;
+ cmd_length_aligned_w <= 0;
+ ar_multiple_full_bursts <= 0;
+ ar_multiple_fixed_bursts <= 0;
+ end
+
+ if (!OPT_UNALIGNED)
+ unaligned_cmd_addr <= 0;
+ end
+
+ // w_status_word
+ // {{{
+ always @(*)
+ begin
+ w_status_word = 0;
+ w_status_word[CBIT_BUSY] = r_busy;
+ w_status_word[CBIT_ERR] = r_err;
+ w_status_word[CBIT_COMPLETE] = r_complete;
+ w_status_word[CBIT_CONTINUOUS] = r_continuous;
+ w_status_word[CBIT_INCREMENT] = !r_increment;
+ w_status_word[20:16] = LGFIFO;
+ end
+ // }}}
+
+ // axil_read_data
+ // {{{
+ always @(posedge i_clk)
+ if (!axil_read_valid || S_AXIL_RREADY)
+ begin
+ axil_read_data <= 0;
+ case(arskd_addr)
+ CMD_CONTROL: axil_read_data <= w_status_word;
+ CMD_ADDRLO: axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0];
+ CMD_ADDRHI: axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ CMD_LENLO: axil_read_data <= wide_length[C_AXIL_DATA_WIDTH-1:0];
+ CMD_LENHI: axil_read_data <= wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ default axil_read_data <= 0;
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+ // }}}
+
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The data FIFO section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ assign reset_fifo = i_reset || (!r_busy && (!r_continuous || r_err));
+
+ // Realign the data (if OPT_UNALIGN) before sending it to the FIFO
+ // {{{
+ // This allows us to handle unaligned addresses.
+ generate if (OPT_UNALIGNED)
+ begin : REALIGN_DATA
+
+ reg r_write_to_fifo;
+ reg [C_AXI_DATA_WIDTH-1:0] last_data,
+ r_write_data;
+ reg [ADDRLSB-1:0] corollary_shift;
+ reg last_valid;
+ reg [ADDRLSB-1:0] realignment;
+
+ always @(*)
+ realignment = cmd_addr[ADDRLSB-1:0];
+
+ initial last_data = 0;
+ always @(posedge i_clk)
+ if (reset_fifo || !unaligned_cmd_addr)
+ last_data <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ last_data <= M_AXI_RDATA >> (realignment * 8);
+
+ initial last_valid = 1'b0;
+ always @(posedge i_clk)
+ if (reset_fifo)
+ last_valid <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ last_valid <= 1'b1;
+ else if (!r_busy)
+ last_valid <= 1'b0;
+
+ assign realign_last_valid = last_valid;
+
+ always @(*)
+ corollary_shift = -realignment;
+
+ always @(posedge i_clk)
+ if (M_AXI_RVALID && M_AXI_RREADY)
+ r_write_data <= (M_AXI_RDATA << (corollary_shift*8))
+ | last_data;
+ else if (!fifo_full)
+ r_write_data <= last_data;
+
+ initial r_write_to_fifo = 1'b0;
+ always @(posedge i_clk)
+ if (reset_fifo)
+ r_write_to_fifo <= 1'b0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ r_write_to_fifo <= last_valid || !unaligned_cmd_addr;
+ else if (!fifo_full)
+ r_write_to_fifo <= 1'b0;
+
+ assign write_to_fifo = r_write_to_fifo;
+ assign write_data = r_write_data;
+
+ end else begin : ALIGNED_DATA
+
+ assign write_to_fifo = M_AXI_RVALID && M_AXI_RREADY;
+ assign write_data = M_AXI_RDATA;
+ assign realign_last_valid = 0;
+
+ end endgenerate
+ // }}}
+
+ assign read_from_fifo = M_AXIS_TVALID && M_AXIS_TREADY;
+ assign M_AXIS_TVALID = !fifo_empty;
+
+ // Write the results to the FIFO
+ // {{{
+ generate if (OPT_TLAST)
+ begin : FIFO_W_TLAST
+ // FIFO section--used if we have to add a TLAST signal to the
+ // data stream
+ // {{{
+ reg pre_tlast;
+ wire tlast;
+
+ // tlast will be set on the last data word of any commanded
+ // burst.
+
+ // Appropriately, pre_tlast = (something) && M_AXI_RVALID
+ // && M_AXI_RREADY && M_AXI_RLAST
+ // We can simplify this greatly, since any time M_AXI_RVALID is
+ // true, we also know that M_AXI_RREADY will be true. This
+ // allows us to get rid of the RREADY condition. Next, we can
+ // simplify the RVALID condition since we'll never write to the
+ // FIFO if RVALID isn't also true. Finally, we can get rid of
+ // M_AXI_RLAST since this is captured by rd_last_remaining.
+ always @(*)
+ pre_tlast = rd_last_remaining;
+
+ if (OPT_UNALIGNED)
+ begin : GEN_UNALIGNED_TLAST
+ reg r_tlast;
+
+ // REALIGN delays the data by one clock period. We'll
+ // also need to delay the last as well.
+ // Note that no one cares what tlast is if write_to_fifo
+ // is zero, allowing us to massively simplify this.
+ always @(posedge i_clk)
+ r_tlast <= pre_tlast;
+
+ assign tlast = r_tlast;
+
+ end else begin : NO_UNALIGNED_TLAST
+
+ assign tlast = pre_tlast;
+ end
+
+
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH+1), .LGFLEN(LGFIFO)
+ // }}}
+ ) sfifo(
+ // {{{
+ i_clk, reset_fifo,
+ write_to_fifo, { tlast, write_data }, fifo_full, fifo_fill,
+ read_from_fifo, { M_AXIS_TLAST, M_AXIS_TDATA }, fifo_empty
+ // }}}
+ );
+ // }}}
+ end else begin : NO_TLAST_FIFO
+
+ // FIFO section, where TLAST is held at 1'b1
+ // {{{
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH), .LGFLEN(LGFIFO)
+ // }}}
+ ) sfifo(
+ // {{{
+ i_clk, reset_fifo,
+ write_to_fifo, write_data, fifo_full, fifo_fill,
+ read_from_fifo, M_AXIS_TDATA, fifo_empty
+ // }}}
+ );
+
+ assign M_AXIS_TLAST = 1'b1;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The incoming AXI (full) protocol section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+
+ // Some counters to keep track of our state
+ // {{{
+
+
+ // Count the number of word writes left to be requested, starting
+ // with the overall command length and then reduced by M_AWLEN on
+ // each address write
+ // {{{
+ always @(*)
+ begin
+ ar_next_remaining = ar_requests_remaining;
+ ar_next_remaining = ar_requests_remaining
+ + { {(LGLENWA-8){phantom_start}},
+ (phantom_start) ? ~M_AXI_ARLEN : 8'h00};
+ end
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ r_pre_start <= 1;
+ else
+ r_pre_start <= 0;
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ ar_needs_alignment <= 0;
+
+ if (|new_wideaddr[ADDRLSB +: LGMAXBURST])
+ begin
+ if (|new_widelen[LGLEN-1:(LGMAXBURST+ADDRLSB)])
+ ar_needs_alignment <= 1;
+ if (~new_wideaddr[ADDRLSB +: LGMAXBURST]
+ < new_widelen[ADDRLSB +: LGMAXBURST])
+ ar_needs_alignment <= 1;
+ end
+ end
+
+ initial ar_none_remaining = 1;
+ initial ar_requests_remaining = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ ar_requests_remaining <= cmd_length_aligned_w;
+ ar_none_remaining <= zero_length;
+ ar_multiple_bursts_remaining
+ <= |cmd_length_aligned_w[LGLENWA-1:LGMAXBURST+1];
+ end else if (cmd_abort || axi_abort_pending)
+ begin
+ ar_requests_remaining <= 0;
+ ar_none_remaining <= 1;
+ ar_multiple_bursts_remaining <= 0;
+
+ end else if (phantom_start)
+ begin
+ // Verilator lint_off WIDTH
+ ar_requests_remaining
+ <= ar_next_remaining;
+ ar_none_remaining <= (ar_next_remaining == 0);
+ ar_multiple_bursts_remaining
+ <= |ar_next_remaining[LGLENWA-1:LGMAXBURST+1];
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // Calculate the maximum possible burst length, ignoring 4kB boundaries
+ // {{{
+ always @(*)
+ addralign = 1+(~cmd_addr[ADDRLSB +: LGMAXBURST]);
+
+ always @(*)
+ begin
+ initial_burstlen = (1<<LGMAXBURST);
+ if (!r_increment)
+ begin
+ initial_burstlen = MAX_FIXED_BURST;
+ if (!ar_multiple_fixed_bursts
+ && !cmd_length_aligned_w[LGMAX_FIXED_BURST])
+ begin
+ initial_burstlen = 0;
+ initial_burstlen[LGMAX_FIXED_BURST-1:0]
+ = cmd_length_aligned_w[
+ LGMAX_FIXED_BURST-1:0];
+ end
+ end else if (ar_needs_alignment)
+ initial_burstlen = { 1'b0, addralign };
+ else if (!ar_multiple_full_bursts
+ && !cmd_length_aligned_w[LGMAXBURST])
+ initial_burstlen = { 1'b0, cmd_length_aligned_w[
+ LGMAXBURST-1:0] };
+ end
+
+ initial r_max_burst = 0;
+ always @(posedge i_clk)
+ if (!r_busy || r_pre_start)
+ begin
+ // Force us to align ourself early
+ // That way we don't need to check for
+ // alignment (again) later
+ r_max_burst <= initial_burstlen;
+ end else if (phantom_start)
+ begin
+ // Verilator lint_off WIDTH
+ if (r_increment || LGMAXBURST <= LGMAX_FIXED_BURST)
+ begin : LIMIT_BY_LGMAXBURST
+ r_max_burst <= (1<<LGMAXBURST);
+
+ if (!ar_multiple_bursts_remaining
+ && ar_next_remaining[LGMAXBURST:0] < (1<<LGMAXBURST))
+ r_max_burst <= { 1'b0, ar_next_remaining[8:0] };
+ end else begin : LIMIT_BY_SIXTEEN
+ r_max_burst <= MAX_FIXED_BURST;
+
+ if (!ar_multiple_bursts_remaining
+ && ar_next_remaining[LGMAXBURST:0] < MAX_FIXED_BURST)
+ r_max_burst <= { 1'b0, ar_next_remaining[LGMAXBURST:0] };
+ end
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // Count the number of bursts outstanding--these are the number of
+ // ARVALIDs that have been accepted, but for which the RVALID && RLAST
+ // has not (yet) been returned.
+ // {{{
+ initial ar_none_outstanding = 1;
+ initial ar_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ ar_bursts_outstanding <= 0;
+ ar_none_outstanding <= 1;
+ end else case ({ phantom_start,
+ M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST })
+ 2'b01: begin
+ ar_bursts_outstanding <= ar_bursts_outstanding - 1;
+ ar_none_outstanding <= (ar_bursts_outstanding == 1);
+ end
+ 2'b10: begin
+ ar_bursts_outstanding <= ar_bursts_outstanding + 1;
+ ar_none_outstanding <= 0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // Are we there yet?
+ // {{{
+ initial rd_reads_remaining = 0;
+ initial rd_none_remaining = 1;
+ initial rd_last_remaining = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ rd_reads_remaining <= cmd_length_aligned_w;
+ rd_last_remaining <= (cmd_length_aligned_w == 1);
+ rd_none_remaining <= (cmd_length_aligned_w == 0);
+ end else if (M_AXI_RVALID && M_AXI_RREADY)
+ begin
+ rd_reads_remaining <= rd_reads_remaining - 1;
+ rd_last_remaining <= (rd_reads_remaining == 2);
+ rd_none_remaining <= (rd_reads_remaining == 1);
+ end
+
+ always @(*)
+ if (!r_busy)
+ w_complete = 0;
+ else if (axi_abort_pending && ar_none_outstanding && !M_AXI_ARVALID)
+ w_complete = 1;
+ else if (r_continuous)
+ w_complete = (rd_none_remaining)||((rd_last_remaining) && M_AXI_RVALID && M_AXI_RREADY);
+ else // if !r_continuous
+ w_complete = (rd_none_remaining && fifo_empty);
+
+ // }}}
+
+ // Are we stopping early? Aborting something ongoing?
+ // {{{
+ initial axi_abort_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ axi_abort_pending <= 0;
+ else begin
+ if (M_AXI_RVALID && M_AXI_RREADY && M_AXI_RRESP[1])
+ axi_abort_pending <= 1;
+ if (cmd_abort)
+ axi_abort_pending <= 1;
+ end
+ // }}}
+
+ // Count the number of uncommited spaces in the FIFO
+ // {{{
+ generate if (OPT_UNALIGNED)
+ begin : GEN_UNALIGNED_BREQ_COUNT
+ reg r_partial_burst_requested;
+
+ initial r_partial_burst_requested = 1'b1;
+ always @(posedge i_clk)
+ if (!r_busy)
+ r_partial_burst_requested <= !unaligned_cmd_addr;
+ else if (phantom_start)
+ r_partial_burst_requested <= 1'b1;
+
+ assign partial_burst_requested = r_partial_burst_requested;
+ end else begin : NO_UNALIGNED_BREQ_COUNT
+
+ assign partial_burst_requested = 1'b1;
+ end endgenerate
+
+ initial rd_uncommitted = (1<<LGFIFO);
+ always @(posedge i_clk)
+ if (reset_fifo)
+ begin
+ rd_uncommitted <= (1<<LGFIFO);
+ end else case ({ phantom_start,
+ M_AXIS_TVALID && M_AXIS_TREADY })
+ 2'b00: begin end
+ 2'b01: begin
+ rd_uncommitted <= rd_uncommitted + 1;
+ end
+ 2'b10: begin
+ // Verilator lint_off WIDTH
+ rd_uncommitted <= rd_uncommitted - (M_AXI_ARLEN + 1)
+ + (partial_burst_requested ? 0 :1);
+ end
+ 2'b11: begin
+ rd_uncommitted <= rd_uncommitted - (M_AXI_ARLEN)
+ + (partial_burst_requested ? 0 :1);
+ // Verilator lint_on WIDTH
+ end
+ endcase
+ // }}}
+
+ // So that we can monitor where we are at, and perhaps restart it
+ // later, keep track of the current address used by the R-channel
+ // {{{
+
+ initial axi_raddr = 0;
+ always @(posedge i_clk)
+ begin
+ if (!r_busy)
+ axi_raddr <= cmd_addr;
+ else if (axi_abort_pending || !r_increment)
+ // Stop incrementing tthe address following an abort
+ axi_raddr <= axi_raddr;
+ else begin
+ if (M_AXI_RVALID && M_AXI_RREADY && !M_AXI_RRESP[1]
+ && (!unaligned_cmd_addr || realign_last_valid))
+ axi_raddr <= axi_raddr + (1<<ADDRLSB);
+ end
+
+ if (!OPT_UNALIGNED)
+ axi_raddr[ADDRLSB-1:0] <= 0;
+ end
+
+ // }}}
+
+ //
+ // }}}
+
+ // start_burst, phantom_start
+ // {{{
+ always @(*)
+ begin
+ start_burst = !ar_none_remaining;
+ if ((rd_uncommitted[LGFIFO:LGMAXBURST] == 0)
+ && ({ 1'b0, rd_uncommitted[LGMAXBURST-1:0] }
+ < r_max_burst))
+ start_burst = 0;
+ if (phantom_start || r_pre_start)
+ // Insist on a minimum of one clock between burst
+ // starts, so we can get our lengths right
+ start_burst = 0;
+ if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ start_burst = 0;
+ if (!r_busy || cmd_abort || axi_abort_pending)
+ start_burst = 0;
+ end
+
+ initial phantom_start = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ phantom_start <= 0;
+ else
+ phantom_start <= start_burst;
+ // }}}
+
+
+ // Calculate ARLEN and ARADDR for the next ARVALID
+ // {{{
+ generate if (LGMAXBURST >= 8)
+ begin : GEN_BIG_AWLEN
+ // Verilator lint_off WIDTH
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ axi_arlen <= initial_burstlen - 1;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arlen <= r_max_burst - 1;
+
+ // Verilator lint_on WIDTH
+ end else begin : GEN_SHORT_AWLEN
+
+ always @(posedge i_clk)
+ begin
+ axi_arlen[7:LGMAXBURST] <= 0;
+ if (!r_busy)
+ axi_arlen[LGMAXBURST:0] <= initial_burstlen - 1;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arlen[LGMAXBURST:0] <= r_max_burst - 1;
+ end
+
+ end endgenerate
+ // }}}
+
+ // Calculate ARADDR for the next ARVALID
+ // {{{
+ initial axi_araddr = 0;
+ always @(posedge i_clk)
+ begin
+ if (M_AXI_ARVALID && M_AXI_ARREADY)
+ begin
+ if (r_increment)
+ // Verilator lint_off WIDTH
+ axi_araddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ <= axi_araddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + (M_AXI_ARLEN + 1);
+ // Verilator lint_on WIDTH
+
+ axi_araddr[ADDRLSB-1:0] <= 0;
+ end
+
+ if (!r_busy)
+ begin
+ axi_araddr <= cmd_addr;
+ end
+
+ if (!OPT_UNALIGNED)
+ axi_araddr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // ARVALID
+ // {{{
+ initial axi_arvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_arvalid <= 0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arvalid <= start_burst;
+ // }}}
+
+ // Set the constant M_AXI_* signals
+ // {{{
+ assign M_AXI_ARVALID= axi_arvalid;
+ assign M_AXI_ARID = AXI_ID;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARLEN = axi_arlen;
+ // Verilator lint_off WIDTH
+ assign M_AXI_ARSIZE = $clog2(C_AXI_DATA_WIDTH)-3;
+ // Verilator lint_on WIDTH
+ assign M_AXI_ARBURST= { 1'b0, r_increment };
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE= 4'b0011;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_ARQOS = 0;
+
+ assign M_AXI_RREADY = 1;
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Optional) clock gating
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_CLKGATE)
+ begin : CLK_GATING
+ // {{{
+ reg gatep, r_clk_active;
+ reg gaten /* verilator clock_enable */;
+
+ // clk_active
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clk_active <= 1'b1;
+ else begin
+ r_clk_active <= 1'b0;
+
+ if (r_busy)
+ r_clk_active <= 1'b1;
+ if (awskd_valid || wskd_valid || arskd_valid)
+ r_clk_active <= 1'b1;
+ if (S_AXIL_BVALID || S_AXIL_RVALID)
+ r_clk_active <= 1'b1;
+
+ if (M_AXIS_TVALID)
+ r_clk_active <= 1'b1;
+ end
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // Gate the clock here locally
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gatep <= 1'b1;
+ else
+ gatep <= clk_active;
+
+ always @(negedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gaten <= 1'b1;
+ else
+ gaten <= gatep;
+
+ assign gated_clk = S_AXI_ACLK && gaten;
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // }}}
+ end else begin : NO_CLK_GATING
+ // {{{
+ // Always active
+ assign clk_active = 1'b1;
+ assign gated_clk = S_AXI_ACLK;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator coverage_off
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_RID,
+ M_AXI_RRESP[0], fifo_full, wskd_strb[2:0], fifo_fill,
+ ar_none_outstanding, S_AXIL_AWADDR[AXILLSB-1:0],
+ S_AXIL_ARADDR[AXILLSB-1:0],
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_widelen
+ };
+ // Verilator coverage_on
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The formal properties for this unit are maintained elsewhere.
+ // This core does, however, pass a full prove (w/ induction) for all
+ // bus properties.
+ //
+ // ...
+ //
+
+
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1;
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Properties of the AXI-stream data interface
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // (These are captured by the FIFO within)
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH),
+ //
+ // ...
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ // ...
+ // }}}
+ );
+
+ //
+ // ...
+ //
+
+ always @(posedge i_clk)
+ if (f_past_valid && $past(S_AXI_ARESETN))
+ begin
+ if ($past(r_busy)||$past(w_cmd_start))
+ begin
+ assert($stable(cmd_length_b));
+ assert($stable(cmd_length_w));
+ assert($stable(cmd_length_aligned_w));
+ end
+ if ($past(r_busy))
+ begin
+ assert($stable(r_increment));
+ assert($stable(r_continuous));
+ end
+ if ($past(r_busy) && $past(r_busy,2))
+ assert($stable(fv_start_addr));
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI master memory interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // ...
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ //
+ .OPT_EXCLUSIVE(1'b0),
+ .OPT_NARROW_BURST(1'b0),
+ //
+ // ...
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(1'b0),
+ .i_axi_awready(1'b0),
+ .i_axi_awid( AXI_ID),
+ .i_axi_awaddr( 0),
+ .i_axi_awlen( 0),
+ .i_axi_awsize( 0),
+ .i_axi_awburst(0),
+ .i_axi_awlock( 0),
+ .i_axi_awcache(0),
+ .i_axi_awprot( 0),
+ .i_axi_awqos( 0),
+ //
+ .i_axi_wvalid(0),
+ .i_axi_wready(0),
+ .i_axi_wdata( 0),
+ .i_axi_wstrb( 0),
+ .i_axi_wlast( 0),
+ //
+ .i_axi_bvalid(1'b0),
+ .i_axi_bready(1'b0),
+ .i_axi_bid( AXI_ID),
+ .i_axi_bresp( 2'b00),
+ //
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_arid( M_AXI_ARID),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arlen( M_AXI_ARLEN),
+ .i_axi_arsize( M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock( M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot( M_AXI_ARPROT),
+ .i_axi_arqos( M_AXI_ARQOS),
+ //
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rlast( M_AXI_RLAST),
+ .i_axi_rresp( M_AXI_RRESP),
+ //
+ // ...
+ //
+ // }}}
+ );
+
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ //
+ // ...
+ //
+ end else begin
+ //
+ // ...
+ //
+
+ assert(rd_uncommitted
+ + ((OPT_UNALIGNED && write_to_fifo) ? 1:0)
+ + fifo_fill == (1<<LGFIFO));
+ if (!r_continuous)
+ assert(fifo_fill == 0 || reset_fifo);
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!OPT_UNALIGNED)
+ begin
+ assert(M_AXI_ARADDR[ADDRLSB-1:0] == 0);
+ end else
+ assert((M_AXI_ARADDR[ADDRLSB-1:0] == 0)
+ ||(M_AXI_ARADDR == fv_start_addr));
+ end
+
+ always @(*)
+ if (!OPT_UNALIGNED || (r_busy && !r_increment))
+ begin
+ assert(cmd_addr[ADDRLSB-1:0] == 0);
+ assert(fv_start_addr[ADDRLSB-1:0] == 0);
+ assert(axi_araddr[ADDRLSB-1:0] == 0);
+ assert(axi_raddr[ADDRLSB-1:0] == 0);
+ end
+
+ //
+ // f_last_addr is the (aligned) address one following the last valid
+ // address read. Once all reading is done, all (aligned) address
+ // pointers should point there.
+ always @(*)
+ begin
+ f_last_addr = { fv_start_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB],
+ {(ADDRLSB){1'b0}} };
+
+ if (r_increment)
+ f_last_addr = f_last_addr + cmd_length_b;
+ if (unaligned_cmd_addr) // Only true if r_increment as well
+ f_last_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ = f_last_addr[C_AXI_ADDR_WIDTH-1:ADDRLSB]+1;
+
+ f_last_addr[ADDRLSB-1:0] = 0;
+
+ f_next_start = fv_start_addr;
+ if (r_increment)
+ f_next_start = f_next_start + cmd_length_b;
+ if (!OPT_UNALIGNED)
+ assert(f_next_start == f_last_addr);
+ end
+
+
+ //
+ // ...
+ //
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Other formal properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Define some helper metrics
+ //
+ initial fv_start_addr = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ fv_start_addr <= cmd_addr;
+
+ always @(*)
+ begin
+ // Metrics defining M_AXI_ARADDR
+ f_araddr_is_aligned = (M_AXI_ARADDR[ADDRLSB+LGMAXBURST-1:0]==0);
+ f_araddr_is_initial = (M_AXI_ARADDR == fv_start_addr);
+ f_araddr_is_final = (M_AXI_ARADDR == f_last_addr);
+
+ //
+ // Metrics to check ARADDR, assuming it had been accepted
+ //
+
+ //
+ // ...
+ //
+ end
+
+ //
+ // fv_ar_requests_remaining ... shadows ar_requests_remaining
+ //
+ // Since ar_requests_remaining drops to zero suddenly on any
+ // axi_abort_pending, we need another counter that we can use
+ // which doesn't have this feature, but which can also be used
+ // to check assertions and intermediate logic against until the
+ // abort takes effect.
+ initial fv_ar_requests_remaining = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ fv_ar_requests_remaining <= cmd_length_aligned_w;
+ end else if (phantom_start)
+ begin
+ // Verilator lint_off WIDTH
+ fv_ar_requests_remaining
+ <= fv_ar_requests_remaining - (M_AXI_ARLEN + 1);
+ // Verilator lint_on WIDTH
+ end
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!axi_abort_pending)
+ begin
+ assert(fv_ar_requests_remaining == ar_requests_remaining);
+ end else
+ assert((ar_requests_remaining == 0)
+ ||(fv_ar_requests_remaining
+ == ar_requests_remaining));
+ end
+
+ always @(*)
+ if(r_busy)
+ begin
+ assert(fv_ar_requests_remaining <= cmd_length_aligned_w);
+ //
+ // ...
+ //
+ end
+
+ //
+ // fv_axi_raddr ... shadows axi_raddr
+ //
+ // The low order bits will match the low order bits of the initial
+ // cmd_addr
+ //
+ initial fv_axi_raddr = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ fv_axi_raddr <= cmd_addr;
+ else if (!r_increment)
+ fv_axi_raddr <= fv_start_addr;
+ else begin
+ if (M_AXI_RVALID && M_AXI_RREADY
+ && (!unaligned_cmd_addr || realign_last_valid))
+ fv_axi_raddr <= fv_axi_raddr + (1<<ADDRLSB);
+ if (!OPT_UNALIGNED)
+ fv_axi_raddr[ADDRLSB-1:0] <= 0;
+ end
+
+ // Constrain start <= axi_raddr <= fv_axi_raddr <= f_last_addr
+ // in spite of any address wrapping
+ always @(*)
+ if (r_busy)
+ begin
+ assert(axi_raddr[ADDRLSB-1:0] == cmd_addr[ADDRLSB-1:0]);
+ assert(axi_abort_pending || fv_axi_raddr == axi_raddr);
+ if (!r_increment)
+ begin
+ assert(fv_axi_raddr == fv_start_addr);
+ assert(axi_raddr == fv_start_addr);
+ end if (!axi_abort_pending)
+ begin
+ if (fv_start_addr <= f_last_addr)
+ begin
+ // Natural order: start < f_raddr < last
+ assert(fv_axi_raddr <= f_last_addr);
+ assert(fv_axi_raddr >= fv_start_addr);
+ end else begin
+ // Reverse order
+ // Either: last < start <= f_raddr
+ // or: f_raddr < last < start
+ assert((fv_axi_raddr >= fv_start_addr)
+ ||(fv_axi_raddr <= f_last_addr));
+ end
+
+ if (fv_start_addr <= fv_axi_raddr)
+ begin
+ // Natural order: start < rad < f_rad < last
+ // or even: last < start < rad < f_rad
+ assert(axi_raddr <= fv_axi_raddr);
+ assert(fv_start_addr <= axi_raddr);
+ end else if (fv_axi_raddr <= f_last_addr)
+ begin
+ // Reverse order: f_raddr < last < start
+ // so either: last < start < raddr
+ // or: raddr < f_raddr < last < start
+ //
+ assert((axi_raddr >= fv_start_addr)
+ || (axi_raddr <= fv_axi_raddr));
+ end
+ end
+ end
+
+ always @(*)
+ if (!r_busy)
+ begin
+ assert(!M_AXI_ARVALID);
+ assert(!M_AXI_RVALID);
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ assert(zero_length == (cmd_length_w == 0));
+
+ always @(*)
+ if (phantom_start)
+ assert(rd_uncommitted >= (M_AXI_ARLEN + 1));
+
+ always @(*)
+ if (zero_length)
+ assert(!r_busy);
+ always @(*)
+ if (r_busy)
+ assert(ar_none_remaining == (ar_requests_remaining == 0));
+ always @(*)
+ assert(ar_none_outstanding == (ar_bursts_outstanding == 0));
+ always @(*)
+ assert(rd_none_remaining == (rd_reads_remaining == 0));
+ always @(*)
+ assert(rd_last_remaining == (rd_reads_remaining == 1));
+
+ always @(*)
+ if (r_complete)
+ assert(!r_busy);
+
+ //
+ // ...
+ //
+
+ //
+ // fifo_availability is a measure of (1<<LGFIFO) minus the current
+ // fifo fill. This does not include what's in the pre-FIFO
+ // logic when OPT_UNALIGNED is true.
+ always @(*)
+ f_fifo_availability = rd_uncommitted;
+
+ always @(*)
+ assert(f_fifo_committed <= (1<<LGFIFO));
+
+ always @(*)
+ assert(f_fifo_availability <= (1<<LGFIFO));
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (!reset_fifo)
+ assert(f_fifo_committed + f_fifo_availability + fifo_fill
+ == (1<<LGFIFO));
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_busy)
+ assert(r_max_burst <= (1<<LGMAXBURST));
+
+ always @(*)
+ if (r_busy && !r_pre_start)
+ assert((r_max_burst > 0) || (ar_requests_remaining == 0));
+
+
+ always @(*)
+ if (phantom_start)
+ assert(M_AXI_ARVALID);
+
+ always @(posedge i_clk)
+ if (phantom_start)
+ begin
+ assert(r_max_burst > 0);
+ assert(M_AXI_ARLEN == $past(r_max_burst)-1);
+ end
+
+
+
+ //
+ // Address checking
+ //
+
+ // Check cmd_addr
+ always @(*)
+ if (r_busy)
+ begin
+ if (r_increment && r_continuous)
+ begin
+ assert(cmd_addr == axi_raddr);
+ end else
+ assert(cmd_addr == fv_start_addr);
+ end
+
+ // Check M_AXI_ARADDR
+ //
+ // ...
+ //
+
+ //
+ // Check M_AXI_ARLEN
+ //
+ // ...
+ //
+
+ //
+ // Constrain the r_maxburst
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_busy)
+ begin
+ assert(r_max_burst <= (1<<LGMAXBURST));
+ //
+ // ...
+ //
+ end
+
+ //
+ // Constrain the length
+ //
+ // ...
+ //
+
+ // Constrain rd_reads_remaining
+ //
+ // ...
+ //
+ always @(*)
+ if (r_busy)
+ begin
+ assert(rd_reads_remaining <= cmd_length_w);
+ //
+ // ...
+ //
+ assert(ar_bursts_outstanding <= rd_reads_remaining);
+ //
+ // ...
+ //
+ end
+
+ //
+ // Constrain the number of requests remaining
+ //
+ // ...
+ //
+
+ //
+ // Make sure our aw_bursts_outstanding counter never overflows
+ // (Given that the counter is as long as the length register, it cannot)
+ //
+ always @(*)
+ begin
+ if (&ar_bursts_outstanding[LGLENWA-1:1])
+ assert(!M_AXI_ARVALID);
+ //
+ // ...
+ //
+ end
+
+ // }}}
+
+ //
+ // Some (fairly) random/unsorted properties
+ // {{{
+
+ always @(*)
+ begin
+ assert(ar_multiple_full_bursts
+ == |cmd_length_w[LGLENW-1:LGMAXBURST]);
+ assert(ar_multiple_fixed_bursts
+ == |cmd_length_w[LGLENW-1:LGMAX_FIXED_BURST]);
+ end
+ //
+ // Match axi_raddr to the faxi_ values
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // 1. All data values must get read and placed into the FIFO, with
+ // none skipped
+ // Captured in logic above(?)
+ //
+ // 2. No addresses skipped. Check the write address against the
+ // write address we are expecting
+ //
+ // ...
+ //
+
+ // 3. If we aren't incrementing addresses, then our current address
+ // should always be the axi address
+ //
+ // ...
+
+ //
+ // 4. Whenever we go from busy to idle, we should raise o_int for one
+ // (and only one) cycle
+ always @(posedge i_clk)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(!o_int);
+ end else
+ assert(o_int == $fell(r_busy));
+
+ //
+ // ...
+ //
+
+
+ // 5. Pick an incoming data value. Choose whether or not to restrict
+ // incoming data not to be that value. If the incoming data is so
+ // restricted then assert that the stream output will never contain that
+ // value.
+ (* anyconst *) reg f_restrict_data;
+ (* anyconst *) reg [C_AXI_DATA_WIDTH-1:0] f_restricted;
+
+ always @(*)
+ if (f_restrict_data && M_AXI_RVALID
+ && (!OPT_UNALIGNED || !unaligned_cmd_addr))
+ assume(M_AXI_RDATA != f_restricted);
+
+ always @(*)
+ if (f_restrict_data && M_AXIS_TVALID
+ && (!OPT_UNALIGNED || !unaligned_cmd_addr))
+ assert(M_AXIS_TDATA != f_restricted);
+
+ //
+ // ...
+ //
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ reg cvr_aborted, cvr_buserr;
+ reg [2:0] cvr_continued;
+
+ initial { cvr_aborted, cvr_buserr } = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ { cvr_aborted, cvr_buserr } <= 0;
+ else if (r_busy && !axi_abort_pending)
+ begin
+ if (cmd_abort && ar_requests_remaining > 0)
+ cvr_aborted <= 1;
+ if (M_AXI_RVALID && M_AXI_RRESP[1] && M_AXI_RLAST)
+ cvr_buserr <= 1;
+ end
+
+ initial cvr_continued = 0;
+ always @(posedge i_clk)
+ if (i_reset || r_err || cmd_abort)
+ cvr_continued <= 0;
+ else begin
+ // Cover a continued transaction across two separate bursts
+ if (r_busy && r_continuous)
+ cvr_continued[0] <= 1;
+ if (!r_busy && cvr_continued[0])
+ cvr_continued[1] <= 1;
+ if (r_busy && cvr_continued[1])
+ cvr_continued[2] <= 1;
+ end
+
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && !i_reset && $fell(r_busy))
+ begin
+ cover( r_err && cvr_aborted);
+ cover( r_err && cvr_buserr);
+ cover(!r_err);
+ if (!r_err && !axi_abort_pending && !cvr_aborted && !cvr_buserr)
+ begin
+ cover(cmd_length_w > 5);
+ cover(cmd_length_w > 8);
+ //
+ // ...
+ //
+ cover(&cvr_continued);
+ cover(&cvr_continued && (cmd_length_w > 2));
+ cover(&cvr_continued && (cmd_length_w > 5));
+ end
+ end
+
+ always @(*)
+ if (!i_reset)
+ cover(!r_err && fifo_fill > 8 && !r_busy);
+
+ always @(*)
+ cover(r_busy);
+
+ always @(*)
+ cover(start_burst);
+
+ always @(*)
+ cover(M_AXI_ARVALID && M_AXI_ARREADY);
+
+ always @(*)
+ cover(M_AXI_RVALID);
+
+ always @(*)
+ cover(M_AXI_RVALID & M_AXI_RLAST);
+ always @(*)
+ if (r_busy)
+ begin
+ cover(!ar_none_remaining);
+ if(!ar_none_remaining)
+ begin
+ cover(1);
+ cover(rd_uncommitted>= {{(LGFIFO-LGMAXBURST){1'b0}}, r_max_burst});
+ if(rd_uncommitted>= {{(LGFIFO-LGMAXBURST){1'b0}}, r_max_burst})
+ begin
+ cover(!phantom_start);
+ cover(phantom_start);
+ end
+ end
+ end
+
+ //
+ // Unaligned cover properties
+ generate if (OPT_TLAST)
+ begin
+ reg [3:0] cvr_lastcount;
+ always @(posedge i_clk)
+ if (i_reset || (r_busy && cmd_length_w <= 2))
+ cvr_lastcount <= 0;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY && M_AXIS_TLAST
+ && !cvr_lastcount[3])
+ cvr_lastcount <= cvr_lastcount + 1;
+
+ always @(*)
+ cover(M_AXIS_TVALID && M_AXIS_TREADY && M_AXIS_TLAST);
+
+ always @(posedge i_clk)
+ cover(o_int && cvr_lastcount > 2);
+
+ end endgenerate
+
+ generate if (OPT_UNALIGNED)
+ begin
+ //
+ // ...
+ //
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && !i_reset &&$fell(r_busy))
+ begin
+ cover(r_err);
+ cover(!r_err);
+ cover(axi_abort_pending);
+ cover(!axi_abort_pending);
+ cover(cvr_aborted);
+ cover(!cvr_aborted);
+ cover(cvr_buserr);
+ cover(!cvr_buserr);
+ cover(!cvr_buserr && !axi_abort_pending);
+ cover(!cvr_buserr && !axi_abort_pending
+ && (cmd_length_w > 2));
+ cover(!r_err && !cvr_aborted && !cvr_buserr
+ && !axi_abort_pending
+ && (cmd_length_w > 2));
+ end
+ end endgenerate
+
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/aximrd2wbsp.v b/rtl/wb2axip/aximrd2wbsp.v
new file mode 100644
index 0000000..07dd7a7
--- /dev/null
+++ b/rtl/wb2axip/aximrd2wbsp.v
@@ -0,0 +1,740 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: aximrd2wbsp.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Bridge an AXI read channel pair to a single wishbone read
+// channel.
+//
+// Rules:
+// 1. Any read channel error *must* be returned to the correct
+// read channel ID. In other words, we can't pipeline between IDs
+// 2. A FIFO must be used on getting a WB return, to make certain that
+// the AXI return channel is able to stall with no loss
+// 3. No request can be accepted unless there is room in the return
+// channel for it
+//
+// Status: Passes a formal bounded model check at 15 steps. Should be
+// ready for a hardware check.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module aximrd2wbsp #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 6, // The AXI id width used for R&W
+ // This is an int between 1-16
+ parameter C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
+ parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width
+ localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH/8),
+ localparam AW = C_AXI_ADDR_WIDTH - AXI_LSBS,
+ parameter LGFIFO = 3,
+ parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
+ parameter [0:0] OPT_SIZESEL = 1
+ // parameter WBMODE = "B4PIPELINE"
+ // Could also be "BLOCK"
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK, // Bus clock
+ input wire S_AXI_ARESETN, // Bus reset
+ // AXI
+ // {{{
+ // AXI read address channel signals
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [7:0] S_AXI_ARLEN,
+ input wire [2:0] S_AXI_ARSIZE,
+ input wire [1:0] S_AXI_ARBURST,
+ input wire [0:0] S_AXI_ARLOCK,
+ input wire [3:0] S_AXI_ARCACHE,
+ input wire [2:0] S_AXI_ARPROT,
+ input wire [3:0] S_AXI_ARQOS,
+
+ // AXI read data channel signals
+ output reg S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire S_AXI_RLAST,
+ output reg [1:0] S_AXI_RRESP,
+ // }}}
+ // Wishbone channel
+ // {{{
+ // We'll share the clock and the reset
+ output reg o_wb_cyc,
+ output reg o_wb_stb,
+ output reg [(AW-1):0] o_wb_addr,
+ output wire [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
+ input wire i_wb_stall,
+ input wire i_wb_ack,
+ input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
+ input wire i_wb_err
+ // }}}
+ // }}}
+ );
+
+ // Register/net definitions
+ // {{{
+ wire w_reset;
+
+ wire lastid_fifo_full, lastid_fifo_empty,
+ resp_fifo_full, resp_fifo_empty;
+ wire [LGFIFO:0] lastid_fifo_fill, resp_fifo_fill;
+
+
+ reg last_stb, last_ack, err_state;
+ reg [C_AXI_ID_WIDTH-1:0] axi_id;
+ reg [7:0] stblen;
+
+ wire skid_arvalid;
+ wire [C_AXI_ID_WIDTH-1:0] skid_arid;// r_id;
+ wire [C_AXI_ADDR_WIDTH-1:0] skid_araddr;// r_addr;
+ wire [7:0] skid_arlen;// r_len;
+ wire [2:0] skid_arsize;// r_size;
+ wire [1:0] skid_arburst;// r_burst;
+ reg fifo_nearfull;
+ wire accept_request;
+
+ reg [1:0] axi_burst;
+ reg [2:0] axi_size;
+ reg [C_AXI_DATA_WIDTH/8-1:0] axi_strb;
+ reg [7:0] axi_len;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_addr;
+ wire [C_AXI_ADDR_WIDTH-1:0] next_addr;
+ wire response_err;
+ wire lastid_fifo_wr;
+ reg midissue, wb_empty;
+ reg [LGFIFO+7:0] acks_expected;
+
+ reg [C_AXI_DATA_WIDTH-1:0] read_data;
+
+ assign w_reset = (S_AXI_ARESETN == 1'b0);
+ // }}}
+
+ // incoming skidbuffer
+ // {{{
+ skidbuffer #(
+ .OPT_OUTREG(0),
+ .DW(C_AXI_ID_WIDTH + C_AXI_ADDR_WIDTH + 8 + 3 + 2)
+ ) axirqbuf(S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_ARVALID, S_AXI_ARREADY,
+ { S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN,
+ S_AXI_ARSIZE, S_AXI_ARBURST },
+ skid_arvalid, accept_request,
+ { skid_arid, skid_araddr, skid_arlen,
+ skid_arsize, skid_arburst });
+ // }}}
+
+ // accept_request
+ // {{{
+ assign accept_request = (!err_state)
+ &&((!o_wb_cyc)||(!i_wb_err))
+ // &&(!lastid_fifo_full)
+ &&(!midissue
+ ||(o_wb_stb && last_stb && !i_wb_stall))
+ &&(skid_arvalid)
+ // One ID at a time, lest we return a bus error
+ // to the wrong AXI master
+ &&(wb_empty ||(skid_arid == axi_id));
+ // }}}
+
+ // o_wb_cyc, o_wb_stb, stblen, last_stb
+ // {{{
+ initial o_wb_cyc = 0;
+ initial o_wb_stb = 0;
+ initial stblen = 0;
+ initial last_stb = 0;
+ always @(posedge S_AXI_ACLK)
+ if (w_reset)
+ begin
+ o_wb_stb <= 1'b0;
+ o_wb_cyc <= 1'b0;
+ end else if (err_state || (o_wb_cyc && i_wb_err))
+ begin
+ o_wb_cyc <= 1'b0;
+ o_wb_stb <= 1'b0;
+ end else if ((!o_wb_stb)||(!i_wb_stall))
+ begin
+ if (accept_request)
+ begin
+ // Process the new request
+ o_wb_cyc <= 1'b1;
+ if (lastid_fifo_full && (!S_AXI_RVALID||!S_AXI_RREADY))
+ o_wb_stb <= 1'b0;
+ else if (fifo_nearfull && midissue
+ && (!S_AXI_RVALID||!S_AXI_RREADY))
+ o_wb_stb <= 1'b0;
+ else
+ o_wb_stb <= 1'b1;
+ end else if (!o_wb_stb && midissue)
+ begin
+ // Restart a transfer once the FIFO clears
+ if (S_AXI_RVALID)
+ o_wb_stb <= S_AXI_RREADY;
+ // end else if ((o_wb_cyc)&&(i_wb_err)||(err_state))
+ end else if (o_wb_stb && !last_stb)
+ begin
+ if (fifo_nearfull
+ && (!S_AXI_RVALID||!S_AXI_RREADY))
+ o_wb_stb <= 1'b0;
+ end else if (!o_wb_stb || last_stb)
+ begin
+ // End the request
+ o_wb_stb <= 1'b0;
+
+ // Check for the last acknowledgment
+ if ((i_wb_ack)&&(last_ack))
+ o_wb_cyc <= 1'b0;
+ if (i_wb_err)
+ o_wb_cyc <= 1'b0;
+ end
+ end
+ // }}}
+
+ // stblen, last_stb, midissue
+ // {{{
+ initial stblen = 0;
+ initial last_stb = 0;
+ initial midissue = 0;
+ always @(posedge S_AXI_ACLK)
+ if (w_reset)
+ begin
+ stblen <= 0;
+ last_stb <= 0;
+ midissue <= 0;
+ end else if (accept_request)
+ begin
+ stblen <= skid_arlen;
+ last_stb <= (skid_arlen == 0);
+ midissue <= 1'b1;
+ end else if (lastid_fifo_wr)
+ begin
+ if (stblen > 0)
+ stblen <= stblen - 1;
+ last_stb <= (stblen == 1);
+ midissue <= (stblen > 0);
+ end
+ // }}}
+
+ // axi_id, axi_burst, axi_size, axi_len
+ // {{{
+ initial axi_size = AXI_LSBS[2:0];
+ initial axi_strb = -1;
+ always @(posedge S_AXI_ACLK)
+ if (accept_request)
+ begin
+ axi_id <= skid_arid;
+ axi_burst <= skid_arburst;
+ axi_size <= skid_arsize;
+ axi_len <= skid_arlen;
+
+ if (OPT_SIZESEL)
+ axi_strb <= (1<<(1<<(skid_arsize)))-1;
+ else
+ axi_strb <= { (C_AXI_DATA_WIDTH/8){1'b1} };
+ end
+`ifdef FORMAL
+ always @(*)
+ case(axi_size)
+ 0: assert(axi_strb == 1);
+ 1: assert((C_AXI_DATA_WIDTH > 8) && (axi_strb == 2'b11));
+ 2: assert((C_AXI_DATA_WIDTH > 16) && (axi_strb == 4'b1111));
+ 3: assert((C_AXI_DATA_WIDTH > 32) && (axi_strb == 8'hff));
+ 4: assert((C_AXI_DATA_WIDTH > 64) && (axi_strb == 16'hffff));
+ 5: assert((C_AXI_DATA_WIDTH > 128) && (axi_strb == 32'hffff_ffff));
+ 6: assert((C_AXI_DATA_WIDTH > 256) && (axi_strb == 64'hffff_ffff_ffff_ffff));
+ default: assert((C_AXI_DATA_WIDTH == 1024) && (&axi_strb));
+ endcase
+`endif
+ // }}}
+
+ // next_addr
+ // {{{
+ axi_addr #(.AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH))
+ next_read_addr(axi_addr, axi_size, axi_burst, axi_len, next_addr);
+ // }}}
+
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (accept_request)
+ axi_addr <= skid_araddr;
+ else if (!i_wb_stall)
+ axi_addr <= next_addr;
+ // }}}
+
+ always @(*)
+ o_wb_addr = axi_addr[(C_AXI_ADDR_WIDTH-1):C_AXI_ADDR_WIDTH-AW];
+
+ // o_wb_sel
+ // {{{
+ generate if (OPT_SIZESEL && C_AXI_DATA_WIDTH > 8)
+ begin : MULTI_BYTE_SEL
+ // {{{
+ assign o_wb_sel = axi_strb << axi_addr[AXI_LSBS-1:0];
+ // }}}
+ end else begin : FULL_WORD_SEL
+ assign o_wb_sel = {(C_AXI_DATA_WIDTH/8){1'b1}};
+
+ // Verilator lint_off UNUSED
+ wire unused_sel;
+ assign unused_sel = &{ 1'b0, axi_strb };
+ // Verilator lint_on UNUSED
+ end endgenerate
+ // }}}
+
+ // lastid_fifo
+ // {{{
+ assign lastid_fifo_wr = (o_wb_stb && (i_wb_err || !i_wb_stall))
+ ||(err_state && midissue && !lastid_fifo_full);
+
+ sfifo #(.BW(C_AXI_ID_WIDTH+1), .LGFLEN(LGFIFO))
+ lastid_fifo(S_AXI_ACLK, w_reset,
+ lastid_fifo_wr,
+ { axi_id, last_stb },
+ lastid_fifo_full, lastid_fifo_fill,
+ S_AXI_RVALID && S_AXI_RREADY,
+ { S_AXI_RID, S_AXI_RLAST },
+ lastid_fifo_empty);
+ // }}}
+
+ // read_data
+ // {{{
+ generate if (OPT_SWAP_ENDIANNESS)
+ begin : SWAP_ENDIANNESS
+ integer ik;
+
+ // AXI is little endian. WB can be either. Most of my WB
+ // work is big-endian.
+ //
+ // This will convert byte ordering between the two
+ always @(*)
+ for(ik=0; ik<C_AXI_DATA_WIDTH/8; ik=ik+1)
+ read_data[ik*8 +: 8]
+ = i_wb_data[(C_AXI_DATA_WIDTH-(ik+1)*8) +: 8];
+
+ end else begin : KEEP_ENDIANNESS
+
+ always @(*)
+ read_data = i_wb_data;
+
+ end endgenerate
+ // }}}
+
+ // resp_fifo
+ // {{{
+ sfifo #(.BW(C_AXI_DATA_WIDTH+1), .LGFLEN(LGFIFO))
+ resp_fifo(S_AXI_ACLK, w_reset,
+ o_wb_cyc && (i_wb_ack || i_wb_err), { read_data, i_wb_err },
+ resp_fifo_full, resp_fifo_fill,
+ S_AXI_RVALID && S_AXI_RREADY,
+ { S_AXI_RDATA, response_err },
+ resp_fifo_empty);
+ // }}}
+
+ // acks_expected
+ // {{{
+ // Count the number of acknowledgements we are still expecting. This
+ // is to support the last_ack calculation in the next process
+ initial acks_expected = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || err_state)
+ acks_expected <= 0;
+ else case({ accept_request, o_wb_cyc && i_wb_ack })
+ 2'b01: acks_expected <= acks_expected -1;
+ 2'b10: acks_expected <= acks_expected+({{(LGFIFO){1'b0}},skid_arlen} + 1);
+ 2'b11: acks_expected <= acks_expected+{{(LGFIFO){1'b0}},skid_arlen};
+ default: begin end
+ endcase
+ // }}}
+
+ // last_ack
+ // {{{
+ // last_ack should be true if the next acknowledgment will end the bus
+ // cycle
+ initial last_ack = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || err_state)
+ last_ack <= 1;
+ else case({ accept_request, i_wb_ack })
+ 2'b01: last_ack <= (acks_expected <= 2);
+ 2'b10: last_ack <= (acks_expected == 0)&&(skid_arlen == 0);
+ 2'b11: last_ack <= (acks_expected < 2)&&(skid_arlen < 2)
+ &&(!acks_expected[0]||!skid_arlen[0]);
+ default: begin end
+ endcase
+ // }}}
+
+ // fifo_nearfull
+ // {{{
+ initial fifo_nearfull = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ fifo_nearfull <= 1'b0;
+ else case({ lastid_fifo_wr, S_AXI_RVALID && S_AXI_RREADY })
+ 2'b10: fifo_nearfull <= (lastid_fifo_fill >= (1<<LGFIFO)-2);
+ 2'b01: fifo_nearfull <= lastid_fifo_full;
+ default: begin end
+ endcase
+ // }}}
+
+ // wb_empty
+ // {{{
+ initial wb_empty = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !o_wb_cyc)
+ wb_empty <= 1'b1;
+ else case({ lastid_fifo_wr, (i_wb_ack || i_wb_err) })
+ 2'b10: wb_empty <= 1'b0;
+ 2'b01: wb_empty <= (lastid_fifo_fill == resp_fifo_fill + 1);
+ default: begin end
+ endcase
+ // }}}
+
+ // S_AXI_RRESP, S_AXI_RVALID
+ // {{{
+ always @(*)
+ begin
+ S_AXI_RRESP[0] = 1'b0;
+ S_AXI_RRESP[1] = response_err || (resp_fifo_empty && err_state);
+
+
+ S_AXI_RVALID = !resp_fifo_empty
+ || (err_state && !lastid_fifo_empty);
+ end
+ // }}}
+
+ // err_state
+ // {{{
+ initial err_state = 0;
+ always @(posedge S_AXI_ACLK)
+ if (w_reset)
+ err_state <= 1'b0;
+ else if ((o_wb_cyc)&&(i_wb_err))
+ err_state <= 1'b1;
+ else if (lastid_fifo_empty && !midissue)
+ err_state <= 1'b0;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_ARLOCK, S_AXI_ARCACHE,
+ S_AXI_ARPROT, S_AXI_ARQOS, resp_fifo_full };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+//
+// The following are the formal properties used to verify this core.
+//
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The following are a subset of the properties used to verify this
+ // core.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam DW = C_AXI_DATA_WIDTH;
+
+ reg f_past_valid;
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions
+ //
+ //
+ always @(*)
+ if (!f_past_valid)
+ assume(w_reset);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Ad-hoc assertions
+ //
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Error state
+ //
+ //
+ always @(*)
+ if (err_state)
+ assert(!o_wb_stb && !o_wb_cyc);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Bus properties
+ //
+ //
+
+ localparam F_LGDEPTH = (LGFIFO>8) ? LGFIFO+1 : 10, F_LGRDFIFO = 72; // 9*F_LGFIFO;
+ //
+ // ...
+ //
+ wire [(F_LGDEPTH-1):0]
+ fwb_nreqs, fwb_nacks, fwb_outstanding;
+
+ fwb_master #(.AW(AW), .DW(C_AXI_DATA_WIDTH), .F_MAX_STALL(2),
+ .F_MAX_ACK_DELAY(3), .F_LGDEPTH(F_LGDEPTH),
+ .F_OPT_DISCONTINUOUS(1))
+ fwb(S_AXI_ACLK, w_reset,
+ o_wb_cyc, o_wb_stb, 1'b0, o_wb_addr, {(DW){1'b0}}, 4'hf,
+ i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
+ fwb_nreqs, fwb_nacks, fwb_outstanding);
+
+ always @(*)
+ if (err_state)
+ assert(fwb_outstanding == 0);
+
+
+ faxi_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_AXI_MAXSTALL(0),
+ .F_AXI_MAXDELAY(0)
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ .i_axi_awready(1'b0),
+ .i_axi_awaddr(0),
+ .i_axi_awlen(8'h0),
+ .i_axi_awsize(3'h0),
+ .i_axi_awburst(2'h0),
+ .i_axi_awlock(1'b0),
+ .i_axi_awcache(4'h0),
+ .i_axi_awprot(3'h0),
+ .i_axi_awqos(4'h0),
+ .i_axi_awvalid(1'b0),
+ //
+ .i_axi_wready(1'b0),
+ .i_axi_wdata(0),
+ .i_axi_wstrb(0),
+ .i_axi_wlast(0),
+ .i_axi_wvalid(1'b0),
+ //
+ .i_axi_bid(0),
+ .i_axi_bresp(0),
+ .i_axi_bvalid(1'b0),
+ .i_axi_bready(1'b0),
+ //
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_arid(S_AXI_ARID),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arlen(S_AXI_ARLEN),
+ .i_axi_arsize(S_AXI_ARSIZE),
+ .i_axi_arburst(S_AXI_ARBURST),
+ .i_axi_arlock(S_AXI_ARLOCK),
+ .i_axi_arcache(S_AXI_ARCACHE),
+ .i_axi_arprot(S_AXI_ARPROT),
+ .i_axi_arqos(S_AXI_ARQOS),
+ .i_axi_arvalid(S_AXI_ARVALID),
+ //
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rid(S_AXI_RID),
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rdata(S_AXI_RDATA),
+ .i_axi_rlast(S_AXI_RLAST),
+ .i_axi_rready(S_AXI_RREADY)
+ //
+ // ...
+ //
+ );
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (!resp_fifo_empty && response_err)
+ assert(resp_fifo_fill == 1);
+
+ always @(*)
+ assert(midissue == ((stblen > 0)||(last_stb)));
+
+ always @(*)
+ if (last_stb && !err_state)
+ assert(o_wb_stb || lastid_fifo_full);
+
+ always @(*)
+ if (last_stb)
+ assert(stblen == 0);
+
+ always @(*)
+ if (lastid_fifo_full)
+ begin
+ assert(!o_wb_stb);
+ assert(!lastid_fifo_wr);
+ end
+
+ always @(*)
+ if (!err_state)
+ begin
+ if (midissue && !last_stb)
+ assert(!last_ack);
+
+ assert(lastid_fifo_fill - resp_fifo_fill
+ == fwb_outstanding);
+
+ if (fwb_outstanding > 1)
+ assert(!last_ack);
+ else if (fwb_outstanding == 1)
+ assert(midissue || last_ack);
+ else if (o_wb_cyc) // && (fwb_outstanding ==0)
+ assert(last_ack == last_stb);
+
+ if (midissue)
+ assert(o_wb_cyc);
+ end
+
+ // wb_empty
+ // {{{
+ always @(*)
+ if (o_wb_cyc)
+ assert(wb_empty == (lastid_fifo_fill == resp_fifo_fill));
+ // }}}
+
+ // !o_wb_cyc, if nothing pending
+ // {{{
+ always @(*)
+ if ((fwb_nacks == fwb_nreqs)&&(!midissue))
+ assert(!o_wb_cyc);
+ // }}}
+
+ always @(*)
+ assert(fwb_outstanding <= (1<<LGFIFO));
+
+ // fifo_nearfull
+ // {{{
+ always @(*)
+ assert(fifo_nearfull == (lastid_fifo_fill >= (1<<LGFIFO)-1));
+ // }}}
+
+ always @(*)
+ if (o_wb_stb)
+ assert(last_ack == (last_stb&& wb_empty));//&&(!i_wb_stall);
+ else if (o_wb_cyc && !midissue)
+ assert(last_ack == (resp_fifo_fill + 1 >= lastid_fifo_fill));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [3:0] cvr_reads, cvr_read_bursts, cvr_rdid_bursts;
+ reg [C_AXI_ID_WIDTH-1:0] cvr_read_id;
+
+ // cvr_reads
+ // {{{
+ initial cvr_reads = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_reads <= 1;
+ else if (i_wb_err)
+ cvr_reads <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && !cvr_reads[3]
+ && cvr_reads > 0)
+ cvr_reads <= cvr_reads + 1;
+ // }}}
+
+ // cvr_read_bursts
+ // {{{
+ initial cvr_read_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_bursts <= 1;
+ else if (S_AXI_ARVALID && S_AXI_ARLEN < 1)
+ cvr_read_bursts <= 0;
+ else if (i_wb_err)
+ cvr_read_bursts <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
+ && !cvr_read_bursts[3] && cvr_read_bursts > 0)
+ cvr_read_bursts <= cvr_read_bursts + 1;
+ // }}}
+
+ // cvr_read_id
+ // {{{
+ initial cvr_read_id = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_read_id <= 1;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ cvr_read_id <= cvr_read_id + 1;
+ // }}}
+
+ // cvr_rdid_bursts
+ // {{{
+ initial cvr_rdid_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_rdid_bursts <= 1;
+ else if (S_AXI_ARVALID && S_AXI_ARLEN < 1)
+ cvr_rdid_bursts <= 0;
+ else if (i_wb_err)
+ cvr_rdid_bursts <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
+ && S_AXI_RID == cvr_read_id
+ && !cvr_rdid_bursts[3] && cvr_rdid_bursts > 0)
+ cvr_rdid_bursts <= cvr_rdid_bursts + 1;
+ // }}}
+
+ always @(*)
+ cover(cvr_reads == 4);
+
+ always @(*)
+ cover(cvr_read_bursts == 4);
+
+ always @(*)
+ cover(cvr_rdid_bursts == 4);
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/aximwr2wbsp.v b/rtl/wb2axip/aximwr2wbsp.v
new file mode 100644
index 0000000..074467c
--- /dev/null
+++ b/rtl/wb2axip/aximwr2wbsp.v
@@ -0,0 +1,751 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: aximwr2wbsp.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Convert the three AXI4 write channels to a single wishbone
+// channel to write the results.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2015-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module aximwr2wbsp #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 6,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ADDR_WIDTH = 28,
+ parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
+ localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AW = C_AXI_ADDR_WIDTH-AXI_LSBS,
+
+ parameter LGFIFO = 5
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK, // System clock
+ input wire S_AXI_ARESETN,
+ // Incoming AXI bus connections
+ // {{{
+ // AXI write address channel signals
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [7:0] S_AXI_AWLEN,
+ input wire [2:0] S_AXI_AWSIZE,
+ input wire [1:0] S_AXI_AWBURST,
+ input wire [0:0] S_AXI_AWLOCK,
+ input wire [3:0] S_AXI_AWCACHE,
+ input wire [2:0] S_AXI_AWPROT,
+ input wire [3:0] S_AXI_AWQOS,
+
+ // AXI write data channel signals
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+
+ // AXI write response channel signals
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [1:0] S_AXI_BRESP,
+ // }}}
+ // Downstream wishbone bus
+ // {{{
+ // We'll share the clock and the reset
+ output reg o_wb_cyc,
+ output reg o_wb_stb,
+ output reg [(AW-1):0] o_wb_addr,
+ output reg [(C_AXI_DATA_WIDTH-1):0] o_wb_data,
+ output reg [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
+ input wire i_wb_stall,
+ input wire i_wb_ack,
+ // input [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
+ input wire i_wb_err
+ // }}}
+
+ // }}}
+ );
+
+ // Register/net declarations
+ // {{{
+ localparam DW = C_AXI_DATA_WIDTH;
+ wire w_reset;
+
+ wire skid_awvalid;
+ reg accept_write_burst;
+ wire [C_AXI_ID_WIDTH-1:0] skid_awid;
+ wire [C_AXI_ADDR_WIDTH-1:0] skid_awaddr;
+ wire [7:0] skid_awlen;
+ wire [2:0] skid_awsize;
+ wire [1:0] skid_awburst;
+ //
+ wire skid_wvalid, skid_wlast;
+ reg skid_wready;
+ wire [C_AXI_DATA_WIDTH-1:0] skid_wdata;
+ wire [C_AXI_DATA_WIDTH/8-1:0] skid_wstrb;
+
+ reg skid_awready;
+ reg [7:0] axi_wlen, wlen;
+ reg [C_AXI_ID_WIDTH-1:0] axi_wid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_waddr;
+ wire [C_AXI_ADDR_WIDTH-1:0] next_addr;
+ reg [1:0] axi_wburst;
+ reg [2:0] axi_wsize;
+
+ reg [LGFIFO+7:0] acks_expected;
+ reg [LGFIFO:0] writes_expected;
+ reg last_ack;
+ reg err_state;
+
+ reg read_ack_fifo;
+ wire [7:0] fifo_ack_ln;
+ reg [8:0] acklen;
+ reg ack_last, ack_err, ack_empty;
+
+ reg [LGFIFO:0] total_fifo_fill;
+ reg total_fifo_full;
+
+ wire wb_ack_fifo_full, wb_ack_fifo_empty;
+ wire [LGFIFO:0] wb_ack_fifo_fill;
+
+ wire err_fifo_full, err_fifo_empty;
+ wire [LGFIFO:0] err_fifo_fill;
+ reg err_fifo_write;
+
+ wire bid_fifo_full, bid_fifo_empty;
+ wire [LGFIFO:0] bid_fifo_fill;
+
+ reg [8:0] next_acklen;
+ reg [1:0] next_acklow;
+
+ assign w_reset = (S_AXI_ARESETN == 1'b0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Skid buffers--all incoming signals go throug skid buffers
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // write address skid buffer
+ // {{{
+ skidbuffer #(
+ .OPT_OUTREG(0),
+ .DW(C_AXI_ADDR_WIDTH+C_AXI_ID_WIDTH+8+3+2))
+ awskid(S_AXI_ACLK, !S_AXI_ARESETN, S_AXI_AWVALID, S_AXI_AWREADY,
+ { S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
+ S_AXI_AWSIZE, S_AXI_AWBURST },
+ skid_awvalid, accept_write_burst,
+ { skid_awid, skid_awaddr, skid_awlen,
+ skid_awsize, skid_awburst });
+ // }}}
+
+ // write channel skid buffer
+ // {{{
+ skidbuffer #(
+`ifdef FORMAL
+ .OPT_PASSTHROUGH(1'b1),
+`endif
+ .OPT_OUTREG(0),
+ .DW(C_AXI_DATA_WIDTH + C_AXI_DATA_WIDTH/8+1))
+ wskid(S_AXI_ACLK, !S_AXI_ARESETN,
+ S_AXI_WVALID, S_AXI_WREADY,
+ { S_AXI_WDATA, S_AXI_WSTRB, S_AXI_WLAST },
+ skid_wvalid, skid_wready,
+ { skid_wdata, skid_wstrb, skid_wlast });
+ // }}}
+
+ // accept_write_burst
+ // {{{
+ always @(*)
+ begin
+ accept_write_burst = (skid_awready)&&(!o_wb_stb || !i_wb_stall)
+ &&(!err_state)&&(skid_awvalid)
+ &&(!total_fifo_full);
+ if (axi_wid != skid_awid && (acks_expected > 0))
+ accept_write_burst = 0;
+ if (!skid_wvalid)
+ accept_write_burst = 0;
+ end
+ // }}}
+
+ // skid_wready
+ // {{{
+ always @(*)
+ skid_wready = (!o_wb_stb || !i_wb_stall || err_state)
+ &&(!skid_awready || accept_write_burst);
+ // }}}
+
+ // skid_awready
+ // {{{
+ initial skid_awready = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ skid_awready <= 1'b1;
+ else if (accept_write_burst)
+ skid_awready <= (skid_awlen == 0)&&(skid_wvalid)&&(skid_wlast);
+ else if (skid_wvalid && skid_wready && skid_wlast)
+ skid_awready <= 1'b1;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Burst unwinding
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // axi_w*, wlen -- properties of the currently active burst
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (accept_write_burst)
+ begin
+ axi_wid <= skid_awid;
+ axi_waddr <= skid_awaddr;
+ axi_wsize <= skid_awsize;
+ axi_wburst <= skid_awburst;
+ axi_wlen <= skid_awlen;
+ wlen <= skid_awlen;
+ end else if (skid_wvalid && skid_wready)
+ begin
+ axi_waddr <= next_addr;
+ if (!skid_awready)
+ wlen <= wlen - 1;
+ end
+ // }}}
+
+ // next_addr
+ // {{{
+ axi_addr #(.AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH))
+ next_write_addr(axi_waddr, axi_wsize, axi_wburst, axi_wlen, next_addr);
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Issue the Wishbone request
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_wb_cyc, o_wb_stb
+ // {{{
+ initial { o_wb_cyc, o_wb_stb } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || err_state || (o_wb_cyc && i_wb_err))
+ begin
+ o_wb_cyc <= 1'b0;
+ o_wb_stb <= 1'b0;
+ end else if (accept_write_burst)
+ begin
+ o_wb_cyc <= 1'b1;
+ o_wb_stb <= skid_wvalid && skid_wready;
+ end else begin
+ if (!o_wb_stb || !i_wb_stall)
+ o_wb_stb <= (!skid_awready)&&(skid_wvalid&&skid_wready);
+ if (o_wb_cyc && last_ack && i_wb_ack && !skid_awvalid)
+ o_wb_cyc <= 0;
+ end
+ // }}}
+
+ always @(*)
+ o_wb_addr = axi_waddr[C_AXI_ADDR_WIDTH-1:AXI_LSBS];
+
+ // o_wb_data, o_wb_sel
+ // {{{
+ generate if (OPT_SWAP_ENDIANNESS)
+ begin : SWAP_ENDIANNESS
+ integer ik;
+
+ always @(posedge S_AXI_ACLK)
+ if (!o_wb_stb || !i_wb_stall)
+ begin
+ for(ik=0; ik<DW/8; ik=ik+1)
+ begin
+ o_wb_data[ik*8 +: 8]
+ <= skid_wdata[(DW/8-1-ik)*8 +: 8];
+ o_wb_sel[ik] <= skid_wstrb[DW/8-1-ik];
+ end
+ end
+
+ end else begin : KEEP_ENDIANNESS
+
+ always @(posedge S_AXI_ACLK)
+ if (!o_wb_stb || !i_wb_stall)
+ begin
+ o_wb_data <= skid_wdata;
+ o_wb_sel <= skid_wstrb;
+ end
+
+ end endgenerate
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FIFO usage tracking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // writes_expected
+ // {{{
+ initial writes_expected = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ writes_expected <= 0;
+ end else case({skid_wvalid && skid_wready && skid_wlast,
+ S_AXI_BVALID && S_AXI_BREADY })
+ 2'b01: writes_expected <= writes_expected - 1;
+ 2'b10: writes_expected <= writes_expected + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // acks_expected
+ // {{{
+ initial acks_expected = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || i_wb_err || err_state)
+ begin
+ acks_expected <= 0;
+ end else case({skid_awvalid && accept_write_burst, {i_wb_ack|i_wb_err}})
+ 2'b01: acks_expected <= acks_expected - 1;
+ 2'b10: acks_expected <= acks_expected + ({{(LGFIFO){1'b0}},skid_awlen} + 1);
+ 2'b11: acks_expected <= acks_expected + {{(LGFIFO){1'b0}},skid_awlen};
+ default: begin end
+ endcase
+ // }}}
+
+ // last_ack
+ // {{{
+ initial last_ack = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || i_wb_err || err_state)
+ begin
+ last_ack <= 1;
+ end else case({skid_awvalid && accept_write_burst, i_wb_ack })
+ 2'b01: last_ack <= (acks_expected <= 2);
+ 2'b10: last_ack <= (acks_expected == 0)&&(skid_awlen == 0);
+ 2'b11: last_ack <= last_ack && (skid_awlen == 0);
+ default: begin end
+ endcase
+
+ // }}}
+
+ // total_fifo_fill
+ // {{{
+ initial total_fifo_fill = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ total_fifo_fill <= 0;
+ else case({ accept_write_burst, S_AXI_BVALID && S_AXI_BREADY })
+ 2'b01: total_fifo_fill <= total_fifo_fill - 1;
+ 2'b10: total_fifo_fill <= total_fifo_fill + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // total_fifo_full
+ // {{{
+ always @(*)
+ total_fifo_full = total_fifo_fill[LGFIFO];
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Return channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // wb_ack_fifo
+ // {{{
+ sfifo #(.BW(8), .LGFLEN(LGFIFO),
+ .OPT_ASYNC_READ(1'b1))
+ wb_ack_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ accept_write_burst, skid_awlen,
+ wb_ack_fifo_full, wb_ack_fifo_fill,
+ read_ack_fifo, fifo_ack_ln, wb_ack_fifo_empty);
+ // }}}
+
+ // read_ack_fifo
+ // {{{
+ always @(*)
+ begin
+ read_ack_fifo = ack_last && (i_wb_ack || i_wb_err);
+ if (err_state || ack_empty)
+ read_ack_fifo = 1;
+ if (wb_ack_fifo_empty)
+ read_ack_fifo = 1'b0;
+ end
+ // }}}
+
+ // next_acklen
+ // {{{
+ always @(*)
+ next_acklen = fifo_ack_ln + ((acklen[0] ? 1:0)
+ + ((i_wb_ack|i_wb_err)? 0:1));
+ // }}}
+
+ // next_acklow
+ // {{{
+ always @(*)
+ next_acklow = fifo_ack_ln[0] + ((acklen[0] ? 1:0)
+ + ((i_wb_ack|i_wb_err)? 0:1));
+ // }}}
+
+ // acklen, ack_last, ack_empty
+ // {{{
+ initial acklen = 0;
+ initial ack_last = 0;
+ initial ack_empty = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || err_state)
+ begin
+ acklen <= 0;
+ ack_last <= 0;
+ ack_empty<= 1;
+ end else if (read_ack_fifo)
+ begin
+ acklen <= next_acklen;
+ ack_last <= (fifo_ack_ln < 2)&&(next_acklow == 1);
+ ack_empty<= (fifo_ack_ln == 0)&&(!acklen[0])
+ &&(i_wb_ack || i_wb_err);
+ end else if (i_wb_ack || i_wb_err)
+ begin
+ if (acklen > 0)
+ acklen <= acklen - 1;
+ ack_last <= (acklen == 2);
+ ack_empty <= ack_last;
+ end
+ // }}}
+
+ // ack_err
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (read_ack_fifo)
+ begin
+ ack_err <= (wb_ack_fifo_empty) || err_state || i_wb_err;
+ end else if (i_wb_ack || i_wb_err || err_state)
+ ack_err <= ack_err || (i_wb_err || err_state);
+ // }}}
+
+ // err_state
+ // {{{
+ initial err_state = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ err_state <= 0;
+ else if (o_wb_cyc && i_wb_err)
+ err_state <= 1;
+ else if ((total_fifo_fill == bid_fifo_fill)
+ &&(total_fifo_fill == err_fifo_fill))
+ err_state <= 0;
+ // }}}
+
+ // err_fifo_write
+ // {{{
+ initial err_fifo_write = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ err_fifo_write <= 0;
+ else if (read_ack_fifo && ack_empty && fifo_ack_ln == 0)
+ err_fifo_write <= (i_wb_ack || i_wb_err || err_state);
+ else if (ack_last)
+ err_fifo_write <= (i_wb_ack || i_wb_err || err_state);
+ else
+ err_fifo_write <= 1'b0;
+ // }}}
+
+ // bid_fifo - Keep track of BID's
+ // {{{
+ sfifo #(.BW(C_AXI_ID_WIDTH), .LGFLEN(LGFIFO))
+ bid_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ skid_wvalid && skid_wready && skid_wlast,
+ (total_fifo_fill == bid_fifo_fill) ? skid_awid:axi_wid,
+ bid_fifo_full, bid_fifo_fill,
+ S_AXI_BVALID & S_AXI_BREADY, S_AXI_BID, bid_fifo_empty);
+ // }}}
+
+ // err_fifo - Keep track of error returns
+ // {{{
+ sfifo #(.BW(1), .LGFLEN(LGFIFO))
+ err_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
+ err_fifo_write, { ack_err || i_wb_err },
+ err_fifo_full, err_fifo_fill,
+ S_AXI_BVALID & S_AXI_BREADY, S_AXI_BRESP[1], err_fifo_empty);
+ // }}}
+
+ assign S_AXI_BVALID = !bid_fifo_empty && !err_fifo_empty;
+ assign S_AXI_BRESP[0]= 1'b0;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_on UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWBURST, S_AXI_AWSIZE,
+ S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT,
+ S_AXI_AWQOS, S_AXI_WLAST,
+ wb_ack_fifo_full, wb_ack_fifo_fill,
+ bid_fifo_full, err_fifo_full,
+ w_reset
+ };
+ // verilator lint_off UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The following are a subset of the properties used to verify this
+ // core
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // Formal only register/wire/parameter definitions
+ // {{{
+ localparam F_LGDEPTH = (LGFIFO>8) ? LGFIFO+1 : 10,
+ F_LGRDFIFO = 72; // 9*F_LGFIFO;
+ reg f_past_valid;
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ localparam F_LGDEPTH = (LGFIFO>8) ? LGFIFO+1 : 10, F_LGRDFIFO = 72; // 9*F_LGFIFO;
+ wire [(F_LGDEPTH-1):0]
+ fwb_nreqs, fwb_nacks, fwb_outstanding;
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Wishbone properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ fwb_master #(
+ // {{{
+ .AW(AW), .DW(C_AXI_DATA_WIDTH), .F_MAX_STALL(2),
+ .F_MAX_ACK_DELAY(3), .F_LGDEPTH(F_LGDEPTH),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) fwb(S_AXI_ACLK, w_reset,
+ // {{{
+ o_wb_cyc, o_wb_stb, 1'b1, o_wb_addr, o_wb_data, o_wb_sel,
+ i_wb_ack, i_wb_stall, {(DW){1'b0}}, i_wb_err,
+ fwb_nreqs, fwb_nacks, fwb_outstanding
+ // }}}
+ );
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI bus properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxi_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_AXI_MAXSTALL(0),
+ .F_AXI_MAXDELAY(0)
+ // }}}
+ ) faxi(.i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ // {{{
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awid( S_AXI_AWID),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awlen( S_AXI_AWLEN),
+ .i_axi_awsize( S_AXI_AWSIZE),
+ .i_axi_awburst(S_AXI_AWBURST),
+ .i_axi_awlock( S_AXI_AWLOCK),
+ .i_axi_awcache(S_AXI_AWCACHE),
+ .i_axi_awprot( S_AXI_AWPROT),
+ .i_axi_awqos( S_AXI_AWQOS),
+ .i_axi_awvalid(S_AXI_AWVALID),
+ //
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ .i_axi_wlast( S_AXI_WLAST),
+ .i_axi_wvalid(S_AXI_WVALID),
+ //
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ //
+ .i_axi_arready(1'b0),
+ .i_axi_arid( {(C_AXI_ID_WIDTH){1'b0}}),
+ .i_axi_araddr({(C_AXI_ADDR_WIDTH){1'b0}}),
+ .i_axi_arlen( 8'h0),
+ .i_axi_arsize( 3'h0),
+ .i_axi_arburst(2'h0),
+ .i_axi_arlock( 1'b0),
+ .i_axi_arcache(4'h0),
+ .i_axi_arprot( 3'h0),
+ .i_axi_arqos( 4'h0),
+ .i_axi_arvalid(1'b0),
+ //
+ .i_axi_rresp( 2'h0),
+ .i_axi_rid( {(C_AXI_ID_WIDTH){1'b0}}),
+ .i_axi_rvalid(1'b0),
+ .i_axi_rdata( {(C_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_rlast( 1'b0),
+ .i_axi_rready(1'b0)
+ //
+ // ...
+ //
+ );
+
+
+ // never_err control(s)
+ // {{{
+ always @(*)
+ if (never_err)
+ begin
+ assume(!i_wb_err);
+ assert(!err_state);
+ if (!skid_awvalid)
+ assert(o_wb_cyc == (acks_expected != 0));
+ if (!skid_awready)
+ assert(o_wb_cyc);
+ if (S_AXI_BVALID)
+ assert(!S_AXI_BRESP[1]);
+ assert(!S_AXI_BRESP[0]);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Cover registers
+ // {{{
+ reg [3:0] cvr_writes, cvr_write_bursts,
+ cvr_wrid_bursts;
+ reg [C_AXI_ID_WIDTH-1:0] cvr_write_id;
+ // }}}
+
+ // cvr_writes
+ // {{{
+ initial cvr_writes = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_writes <= 1;
+ else if (i_wb_err)
+ cvr_writes <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY && !cvr_writes[3]
+ && cvr_writes > 0)
+ cvr_writes <= cvr_writes + 1;
+ // }}}
+
+ // cvr_write_bursts
+ // {{{
+ initial cvr_write_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_write_bursts <= 1;
+ else if (S_AXI_AWVALID && S_AXI_AWLEN < 1)
+ cvr_write_bursts <= 0;
+ else if (i_wb_err)
+ cvr_write_bursts <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY
+ && !cvr_write_bursts[3] && cvr_write_bursts > 0)
+ cvr_write_bursts <= cvr_write_bursts + 1;
+ // }}}
+
+ // cvr_write_id
+ // {{{
+ initial cvr_write_id = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_write_id <= 1;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ cvr_write_id <= cvr_write_id + 1;
+ // }}}
+
+ // cvr_wrid_bursts
+ // {{{
+ initial cvr_wrid_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_wrid_bursts <= 1;
+ else if (S_AXI_AWVALID && S_AXI_AWLEN < 1)
+ cvr_wrid_bursts <= 0;
+ else if (i_wb_err)
+ cvr_wrid_bursts <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY
+ && S_AXI_BID == cvr_write_id
+ && !cvr_wrid_bursts[3] && cvr_wrid_bursts > 0)
+ cvr_wrid_bursts <= cvr_wrid_bursts + 1;
+ // }}}
+
+ always @(*) cover(cvr_writes == 4);
+ always @(*) cover(cvr_write_bursts == 4);
+ always @(*) cover(cvr_wrid_bursts == 4);
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axiperf.v b/rtl/wb2axip/axiperf.v
new file mode 100644
index 0000000..f2498e6
--- /dev/null
+++ b/rtl/wb2axip/axiperf.v
@@ -0,0 +1,1603 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axiperf
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Measure the performance of a high speed AXI interface. The
+// {{{
+// following monitor requires connecting to both an AXI-lite slave
+// interface, as well as a second AXI interface as a monitor. The AXI
+// monitor interface is read only, and (ideally) shouldn't be corrupted by
+// the inclusion of the AXI-lite interface on the same bus.
+//
+// The core works by counting clock cycles in a fashion that should
+// supposedly make it easy to calculate 1) throughput, and 2) lag.
+// Moreover, the counters are arranged such that after the fact, the
+// various contributors to throughput can be measured and evaluted: was
+// the slave the holdup? Or was it the master?
+//
+// To use the core, connect it and build your design. Then write a '3'
+// to register 15 (address 60). Once the bus returns to idle, the core
+// will begin capturing data and statistics. When done, write a '0' to
+// the same register. This will create a stop request. Once the bus
+// comes to a stop, the core will stop accumulating values into its
+// statistics. Those statistics can then be read out from the AXI-lite
+// bus and analyzed.
+// }}}
+// Goals:
+// {{{
+// My two biggest goals are to measure throughput and lag. Defining those
+// two measures, of course, is half the battle. The other half of the
+// battle is knowing which side to blame for any particular issue.
+//
+// Let's start with the total time required for any transaction. This
+// equals the time from the indication of a request to the last response.
+// We'll use a linear model to describe this transaction time:
+//
+// Transaction time = Latency + (Beats in transaction) / Throughput
+//
+// The goal of this core is to help you identify latency and throughput
+// numbers.
+//
+// One measure might be to take the total number of clock cycles, from when
+// the core was enabled to when it was disabled, and to divide by the
+// number of beats transmitted.
+//
+// (Poor) Throughput = (Total beats transferred) / (total time)
+//
+// In a heavily used bus, this might be a good enough measure. However,
+// this is a poor measure for most systems where the bus is idle most of
+// the time. Instead, it might be nice to start the measurement early
+// on during some task, and conclude it much later. In the meantime, the
+// bus might go from idle to busy and back again many times. For example,
+// you don't want to copy information from the disk drive if you haven't
+// made a request of the controller. For these reasons, we try to achieve
+// a better measurement.
+//
+// Here's the basic approach: we'll look at all of the clocks associated
+// with any particular type of transaction, and lump them into a couple
+// of categories: latency limiting clocks and throughput limiting clocks.
+// We'll then divide the latency limiting clocks by the number of bursts
+// that have taken place, and divide the total number of beats by the
+// time taken to transmit them.
+//
+// Latency = (latency measures) / (bursts)
+// Throughput = (beats) / (transmission duration, inc. beats)
+//
+// In general, we'll define the transmission duration as the time from the
+// first clock cycle that RVALID (or WVALID) is raised until the final
+// cycle when RVALID && RREADY && RLAST (or WVALID && WREADY && WLAST).
+// Unless we know otherwise, all clock cycles between these two will
+// be marked as a transmission duration clock cycles. The exception
+// to this rule, however, is the W* channel where one or two W*
+// transactions might take place prior to the first AW* transaction. In
+// this case, any idle cycles during this time are marked as a latency
+// measure, not a throughput measure of transmission duration.
+//
+// Latency measures, on the other hand, are anything that appear to be
+// burst related--such as the time from the request to the first
+// RVALID (or WVALID), or similarly the time from the last WVALID && WLAST
+// until the final BVALID && BREADY.
+//
+// These measures are listed in more detail below.
+//
+// Certain measures below are marked as *ORTHOGONAL*. These are perhaps
+// better known as (independent), but I started calling them orthogonal
+// and ... will probably do so for some time. Orthogonal measures are
+// those that don't overlap. For example, if you just counted AWVALID
+// && AWREADY (bursts) and WVALID && WREADY clock cycles (beats), you might
+// get a big overlap between the two and so not know which to count. Not
+// so with the orthogonal measures.
+//
+// Further, at the end of every list of orthogonal measures is a metric
+// that can be used to calculate total cycles used--that way you know
+// how the measures relate.
+// }}}
+// Registers
+// {{{
+// 0: Active time
+// Number of clock periods that the performance monitor has been
+// accumulating data for. This register has a protection measure
+// built into it: if it ever overflows, it will report an
+// 8'hffff_fff value. This is your indication that other values
+// from within this performance measure are not to be trusted.
+// Either increase LGCNT or try a shorter collection time to fix
+// such a condition.
+//
+// 4: Max bursts
+// Bits 31:24 -- the maximum number of outstanding write bursts at any
+// given time. A write burst begins with either
+// AWVALID && AWREADY or WVALID && WREADY and ends with
+// BVALID && BREADY. This will be the maximum of the two.
+// Bits 23:16 -- the maximum number of outstanding read bursts at any
+// given time. A read burst begins with ARVALID &&ARREADY,
+// and ends with RVALID && RLAST
+// Bits 15: 8 -- the maximum write burst size seen, as captured by AWLEN
+// Bits 7: 0 -- the maximum read burst size seen, as captured by ARLEN
+// 8: Write idle cycles
+// Number of cycles where the write channel is totally idle.
+// *ORTHOGONAL*
+// 12: AWBurst count
+// Number of AWVALID && AWREADY's
+// 16: Write beat count
+// Number of write beats, WVALID && WREADYs
+// 20: AW Byte count
+// Number of bytes written, as recorded by the AW* channel (not the
+// W* channel and WSTRB signals)
+// 24: Write Byte count
+// Number of bytes written, as recorded by the W* channel and the
+// non zero WSTRB's
+// 28: Write slow data
+// Number of cycles where a write has started, that is WVALID
+// and WREADY (but !WLAST) have been seen, but yet WVALID is now
+// low. These are only counted if a write address request has
+// already been received--otherwise this would be considered
+// a latency measure on the AW* channel.
+// *ORTHOGONAL*
+// 32: wr_stall--Write stalls
+// Counts the number of cycles where WVALID && !WREADY, but
+// only if AWVALID is true or has been true. This is to
+// distinguish from stalls which may take place before AWVALID,
+// where the slave may be waiting on AWVALID (lag) versus
+// unable to handle the throuhgput. (Those are counted under
+// wr_early_stall below ...)
+// *ORTHOGONAL*
+// 36: wr_addr_lag--Write address channel lagging
+// Counts the number of cycles where the write data has been
+// present on the channel prior to the write address. This
+// includes cycles where AWVALID is true or stalled, just not
+// cycles where WVALID is also true--since those have already
+// been counted.
+// *ORTHOGONAL*
+// 40: wr_data_lag--Write data laggging
+// The AWVALID && AWREADY has been received, but no data has
+// yet been received for this write burst and WVALID remains
+// low. (i.e., no BVALIDs are pending either.) This is a
+// lag measure since WVALID hasn't shown up (yet) to start sending
+// data.
+// *ORTHOGONAL*
+// 44: wr_awr_early--AWVALID && AWREADY, but only if !WVALID and
+// no AWVALID has yet been received. This is a lag measure since
+// AWVALID is preceding WVALID.
+// *ORTHOGONAL*
+// 48: wr_early_beat--WVALID && WREADY && !AWVALID, and also prior to
+// any AWVALID. This value is double counted in the write
+// beat counts, so you will need to subtract the two if you
+// wish to separate them.
+// *Otherwise ORTHOGONAL*
+// 52: wr_addr_stall--AWVALID && !AWREADY, but only if !WVALID and
+// no AWVALID has yet been received. (This keeps it from being
+// double counted as part of a throughput measure.)
+// *ORTHOGONAL*
+// 56: wr_early_stall--WVALID && !WREADY, but only if this burst has
+// not yet started and no AWVALID has yet been received. That
+// makes this a lag measure, since the slave is likely waiting
+// for the address before starting to process the burst.
+// *ORTHOGONAL*
+// 60: b_lag_count
+// Counts the number of cycles between the last accepted AWVALID
+// and WVALID && WLAST and its corresponding BVALID. This is
+// the number of cycles where BVALID could be high in response
+// to any burst, but yet where it isn't. To avoid interfering
+// with the throughput measure, this excludes any cycles where
+// WVALID is also true.
+// *ORTHOGONAL*
+// 64: b_stall_count
+// Number of cycles where BVALID && !BREADY. This could be a
+// possible indication of backpressure in the interconnect.
+// This also excludes any cycles where WVALID is also true.
+// *ORTHOGONAL*
+// 68: b_end_count
+// Number of cycles where BVALID && BREADY, but where nothing
+// else is outstanding and where no WVALID is being requested.
+// This just completes our measurements, making sure that all
+// beats of a transaction are properly accounted for.
+// *ORTHOGONAL*
+//
+// 72: Write Bias (Signed)
+// Total number of cycles between the first AWVALID and the
+// first WVALID, minus the total number of cycles between the
+// first WVALID and the first AWVALID. This is a measure of
+// how often AWV clock cycles come before the first WV cycle and
+// by how much. To make use of this statistic, divide it by the
+// total number of bursts for the average distance between the
+// first AWV and the first WV. Note that unlike many of these
+// statistics, this value is signed. Negative distances are
+// possible if the first WV tends to precede the first AWV.
+//
+// Total write cycles = (active_time - wr_b_end_count - wr_idle_cycles)
+// = (wr_addr_lag+wr_data_lag+wr_awr_early+wr_early_beat
+// + wr_addr_stall + wr_b_lag_count + wr_b_stall_count)
+// + (wr_slow_data + wr_stall + wr_beats - wr_early_beats)
+//
+// Latency = (wr_addr_lag + wr_data_lag + wr_awr_early + wr_early_beat
+// + wr_addr_stall + wr_b_lag + wr_b_stall) / WR BURSTS
+// Throughput= (wr_beats) /
+// (wr_slow_data + wr_stall + wr_beats - wr_early_beats)
+//
+// 80: Read idle cycles
+// Number of clock cycles, while the core is collecting, where
+// nothing is happening on the read channel--ARVALID is low,
+// nothing is outstanding, etc. *ORTHOGONAL*
+// 84: Max responding bursts
+// This is the maximum number of bursts that have been responding
+// at the same time, as counted by the maximum number of ID's
+// which have seen an RVALID but not RLAST. It's an estimate of
+// how out of order the channel has become.
+// 88: Read burst count
+// The total number of RVALID && RREADY && RLAST's seen
+// 92: Read beat count
+// The total number of beats requested, as measured by
+// RVALID && RREADY (or equivalently by ARLEN ... but we measure
+// RVALID && RREADY here). *ORTHOGONAL*
+// 96: Read byte count
+// The total number of bytes requested, as measured by ARSIZE
+// and ARLEN.
+// 100: AR cycles
+// Total number of cycles where the interface is idle, but yet
+// ARVALID && ARREADY are both true. Yes, it'll be busy on the
+// next cycle, but we still need to count them.
+// *ORTHOGONAL*
+// 104: AR stalls
+// Total number of clock cycles where ARVALID && !ARREADY, but
+// only under the condition that nothing is currently outstanding.
+// If the master refuses to allow a second AR* burst into the
+// pipeline, this should show in the maximum number of outstanding
+// read bursts ever allowed. *ORTHOGONAL*
+// 108: R stalls
+// Total number of clock cycles where RVALID && !RREADY. This is
+// an indication of a master that has issued more read requests
+// than it can process, and so it is suffering from internal
+// back pressure. *ORTHOGONAL*
+// 112: Read Lag counter
+// Counts the number of clock cycles where an outstanding read
+// request exists, but for which no data has (yet) been returned.
+// *ORTHOGONAL*
+// 116: Slow link
+// Counts the number of clock cycles where RVALID is low, but yet
+// a burst return has already started but not yet been completed.
+// *ORTHOGONAL*
+//
+// If we've done this right, then
+//
+// active_time == read idle cycles (channel is idle)
+// + read_beat_count (data is transferred)_
+// + r stalls (Master isn't ready)
+// + lag counter (No data is ready)
+// + slow link (Slave isn't ready))
+// + rd_ar_stalls (Slave not ready for AR*)
+// + rd_ar_cycles (Slave accepted AR*, o.w. idle)
+//
+// We can then measure read throughput as the number of
+// active cycles (active time - read idle counts) divided by the
+// number of bytes (or beats) transferred (depending upon the
+// units you want.
+//
+// Lag would be measured by the lag counter divided by the number
+// of read bursts.
+//
+// 120: Read first lag
+// This is another attempt to estimate the latency in a channel.
+// Its a measure from ARVALID on an idle channel until RVALID.
+// Other latency measures (above) can get confused by multiple
+// transactions in progress at a time. This artificially lowers
+// the latency from what the channel would otherwise produce,
+// counting latency cycles as throughput cycles. On the other
+// hand, if we count the number of cycles from idle to the first
+// return, we can get a more conventional measure of latency.
+// To use this statistic to get latency, divide it by the total
+// number of AR Cycles. This works because those cycles are
+// *only* counted if the channel is otherwise idle.
+//
+// 124: Control register
+// Write a 1 to this register to start recording, and a 0 to this
+// register to stop. Writing a 2 will clear the counters as
+// well.
+//
+// This performance monitor depends on certain counters to make
+// sure it can recognize when the bus is idle. If any of these
+// counters overflow, then the core cannot tell when to start
+// or stop counting and so all performance measures will then be
+// invalid. If this happens, the perf_error (bit 3) will be set.
+// This bit can only be cleared on a full bus reset--often
+// requiring a power cycle.
+//
+// Performance:
+// Write Throughput = (Wr Beats) / (Wr Beats + WrStalls + WrSlow);
+// Read Throughput = (Rd Beats) / (Rd Beats + R Stalls + RSlow);
+// Read Latency = (AR Stalls + RdLag) ./ (Rd Bursts)
+// }}}
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axiperf #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ parameter C_AXIL_ADDR_WIDTH = 7,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 4,
+ parameter [0:0] OPT_LOWPOWER = 0,
+ parameter LGCNT = 32
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ //
+ //
+ // The AXI Monitor interface
+ //
+ input wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ input wire [7:0] M_AXI_AWLEN,
+ input wire [2:0] M_AXI_AWSIZE,
+ input wire [1:0] M_AXI_AWBURST,
+ input wire M_AXI_AWLOCK,
+ input wire [3:0] M_AXI_AWCACHE,
+ input wire [2:0] M_AXI_AWPROT,
+ input wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ input wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ input wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ input wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ //
+ input wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ input wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ input wire [7:0] M_AXI_ARLEN,
+ input wire [2:0] M_AXI_ARSIZE,
+ input wire [1:0] M_AXI_ARBURST,
+ input wire M_AXI_ARLOCK,
+ input wire [3:0] M_AXI_ARCACHE,
+ input wire [2:0] M_AXI_ARPROT,
+ input wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ input wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP
+ //
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam ADDRLSB = $clog2(C_AXIL_DATA_WIDTH/8);
+ wire i_reset = !S_AXI_ARESETN;
+
+ // AXI signaling
+ // {{{
+ wire axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr;
+ //
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ wire awskd_valid, wskd_valid;
+ wire arskd_valid;
+ // }}}
+
+ reg r_idle_bus, triggered, stop_request,
+ clear_request, start_request;
+ wire idle_bus;
+ reg [LGCNT:0] active_time;
+ reg wr_aw_err, wr_w_err, rd_err, perf_err;
+
+
+ // Write measures
+ // {{{
+ reg [7:0] wr_max_burst_size;
+ reg [LGCNT-1:0] wr_awburst_count, wr_wburst_count, wr_beat_count;
+ reg [LGCNT-1:0] wr_aw_byte_count, wr_w_byte_count;
+ reg [7:0] wr_aw_outstanding, wr_w_outstanding,
+ wr_aw_max_outstanding, wr_w_max_outstanding,
+ wr_max_outstanding, wr_now_outstanding;
+ reg wr_aw_zero_outstanding, wr_w_zero_outstanding,
+ wr_in_progress;
+ reg [LGCNT-1:0] wr_idle_cycles,
+ wr_b_lag_count, wr_b_stall_count, wr_b_end_count,
+ wr_slow_data, wr_stall, wr_early_beat, // wr_beat,
+ wr_addr_lag, wr_data_lag, wr_awr_early,
+ wr_bias, wr_addr_stall, wr_early_stall;
+ reg[C_AXI_DATA_WIDTH/8:0] wstrb_count;
+ // }}}
+
+ // Read measures
+ // {{{
+ reg [LGCNT-1:0] rd_idle_cycles, rd_lag_counter, rd_slow_link,
+ rd_burst_count, rd_byte_count, rd_beat_count,
+ rd_ar_stalls, rd_r_stalls, rd_ar_cycles;
+ reg [7:0] rd_outstanding_bursts, rd_max_burst_size,
+ rd_max_outstanding_bursts;
+ reg [7:0] rd_outstanding_bursts_id [0:(1<<C_AXI_ID_WIDTH)-1];
+ reg [(1<<C_AXI_ID_WIDTH)-1:0] rd_nonzero_outstanding_id,
+ rd_bursts_in_flight;
+ reg [C_AXI_ID_WIDTH:0] rd_total_in_flight, rd_responding,
+ rd_max_responding_bursts;
+ reg [LGCNT-1:0] rd_first_lag;
+ reg rd_first;
+ // }}}
+
+ integer ik;
+ genvar gk;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_ADDR_WIDTH-ADDRLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIL_ADDR_WIDTH-ADDRLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (idle_bus)
+ clear_request <= 1'b0;
+ if (!clear_request && idle_bus)
+ begin
+ start_request <= 0;
+ stop_request <= 0;
+ end
+
+ if (axil_write_ready)
+ begin
+ case(awskd_addr)
+ 5'h1f: if (wskd_strb[0]) begin
+ // Start, stop, clear, reset
+ //
+ clear_request <= (clear_request && !idle_bus)
+ || (wskd_data[1] && !wskd_data[0]);
+ stop_request <= !wskd_data[0];
+ start_request <= wskd_data[0] && (!stop_request);
+ end
+ default: begin end
+ endcase
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ clear_request <= 1'b0;
+ stop_request <= 1'b0;
+ start_request <= 1'b0;
+ end
+ end
+
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ begin
+ axil_read_data <= 0;
+ case(arskd_addr)
+ 5'h00: begin
+ // {{{
+ if (!active_time[LGCNT])
+ axil_read_data[LGCNT-1:0] <= active_time[LGCNT-1:0];
+ else
+ // OVERFLOW!
+ axil_read_data <= -1;
+ end
+ // }}}
+ 5'h01: axil_read_data <= { wr_max_outstanding,
+ rd_max_outstanding_bursts,
+ wr_max_burst_size,
+ rd_max_burst_size };
+ 5'h02: axil_read_data[LGCNT-1:0] <= wr_idle_cycles;
+ 5'h03: axil_read_data[LGCNT-1:0] <= wr_awburst_count;
+ 5'h04: axil_read_data[LGCNT-1:0] <= wr_beat_count;
+ 5'h05: axil_read_data[LGCNT-1:0] <= wr_aw_byte_count;
+ 5'h06: axil_read_data[LGCNT-1:0] <= wr_w_byte_count;
+ //
+ 5'h07: axil_read_data[LGCNT-1:0] <= wr_slow_data;
+ 5'h08: axil_read_data[LGCNT-1:0] <= wr_stall;
+ 5'h09: axil_read_data[LGCNT-1:0] <= wr_addr_lag;
+ 5'h0a: axil_read_data[LGCNT-1:0] <= wr_data_lag;
+ 5'h0b: axil_read_data[LGCNT-1:0] <= wr_awr_early;
+ 5'h0c: axil_read_data[LGCNT-1:0] <= wr_early_beat;
+ 5'h0d: axil_read_data[LGCNT-1:0] <= wr_addr_stall;
+ 5'h0e: axil_read_data[LGCNT-1:0] <= wr_early_stall;
+ 5'h0f: axil_read_data[LGCNT-1:0] <= wr_b_lag_count;
+ 5'h10: axil_read_data[LGCNT-1:0] <= wr_b_stall_count;
+ 5'h11: axil_read_data[LGCNT-1:0] <= wr_b_end_count;
+ //
+ 5'h12: begin
+ // {{{
+ // Sign extend the write bias
+ if (wr_bias[LGCNT-1])
+ axil_read_data <= -1;
+ axil_read_data[LGCNT-1:0] <= wr_bias;
+ end
+ // }}}
+ // 5'h13: axil_read_data[LGCNT-1:0] <= wr_first_lag;
+ //
+ 5'h14: axil_read_data[LGCNT-1:0] <= rd_idle_cycles;
+ 5'h15: axil_read_data <= {
+ {(C_AXIL_DATA_WIDTH-C_AXI_ID_WIDTH-1){1'b0}},
+ rd_max_responding_bursts };
+ 5'h16: axil_read_data[LGCNT-1:0] <= rd_burst_count;
+ 5'h17: axil_read_data[LGCNT-1:0] <= rd_beat_count;
+ 5'h18: axil_read_data[LGCNT-1:0] <= rd_byte_count;
+ 5'h19: axil_read_data[LGCNT-1:0] <= rd_ar_cycles;
+ 5'h1a: axil_read_data[LGCNT-1:0] <= rd_ar_stalls;
+ 5'h1b: axil_read_data[LGCNT-1:0] <= rd_r_stalls;
+ 5'h1c: axil_read_data[LGCNT-1:0] <= rd_lag_counter;
+ 5'h1d: axil_read_data[LGCNT-1:0] <= rd_slow_link;
+ 5'h1e: axil_read_data[LGCNT-1:0] <= rd_first_lag;
+ 5'h1f: axil_read_data <= {
+ // pending_idle,
+ // pending_first_burst,
+ // cleared,
+ 28'h0, perf_err,
+ triggered,
+ clear_request,
+ start_request
+ };
+ default: begin end
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+
+ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXI_DATA_WIDTH-1:0] prior_data;
+ input [C_AXI_DATA_WIDTH-1:0] new_data;
+ input [C_AXI_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI performance counters
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // triggered
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ triggered <= 0;
+ else if (idle_bus)
+ begin
+ if (start_request && !clear_request)
+ triggered <= 1'b1;
+ if (stop_request)
+ triggered <= 0;
+ end
+ // }}}
+
+ // active_time : count number of cycles while triggered
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ active_time <= 0;
+ else if (triggered)
+ begin
+ if (!active_time[LGCNT])
+ active_time <= active_time + 1;
+ end
+ // }}}
+
+ // idle_bus : Can we start or stop our couters? Can't if not idle
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_idle_bus <= 1;
+ else if (M_AXI_AWVALID || M_AXI_WVALID || M_AXI_ARVALID || perf_err)
+ r_idle_bus <= 0;
+ else if ((wr_aw_outstanding
+ ==((M_AXI_BVALID && M_AXI_BREADY) ? 1:0))
+ && (wr_w_outstanding == ((M_AXI_BVALID && M_AXI_BREADY) ? 1:0))
+ && (rd_outstanding_bursts
+ ==((M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST)? 1:0)))
+ r_idle_bus <= 1;
+
+ assign idle_bus = r_idle_bus && !M_AXI_AWVALID && !M_AXI_WVALID
+ && !M_AXI_ARVALID;
+ // }}}
+
+ // perf_err
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ perf_err <= 0;
+ else if (wr_aw_err || wr_w_err || rd_err)
+ perf_err <= 1;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write statistics
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // wr_max_burst_size: max of all AWLEN values
+ // {{{
+ initial wr_max_burst_size = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_max_burst_size <= 0;
+ else if (triggered)
+ begin
+ if (M_AXI_AWVALID && M_AXI_AWLEN > wr_max_burst_size)
+ wr_max_burst_size <= M_AXI_AWLEN;
+ end
+ // }}}
+
+ // wr_awburst_count -- count AWVALID && AWREADY
+ // {{{
+ initial wr_awburst_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_awburst_count <= 0;
+ else if (triggered && M_AXI_AWVALID && M_AXI_AWREADY)
+ wr_awburst_count <= wr_awburst_count + 1;
+ // }}}
+
+ // wr_wburst_count -- count of WVALID && WLAST && WREADY
+ // {{{
+ initial wr_wburst_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_wburst_count <= 0;
+ else if (triggered && M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST)
+ wr_wburst_count <= wr_wburst_count + 1;
+ // }}}
+
+ // wr_beat_count -- count of WVALID && WREADY
+ // {{{
+ initial wr_beat_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_beat_count <= 0;
+ else if (triggered && M_AXI_WVALID && M_AXI_WREADY)
+ wr_beat_count <= wr_beat_count + 1;
+ // }}}
+
+ // wr_aw_byte_count : count of (AWLEN+1)<<AWSIZE
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_aw_byte_count <= 0;
+ else if (triggered && M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ wr_aw_byte_count <= wr_aw_byte_count
+ + (({ 24'b0, M_AXI_AWLEN}+32'h1) << M_AXI_AWSIZE);
+ end
+ // }}}
+
+ // wstrb_count -- combinatorial, current active strobe count
+ // {{{
+ always @(*)
+ begin
+ wstrb_count = 0;
+ for(ik=0; ik<C_AXI_DATA_WIDTH/8; ik=ik+1)
+ if (M_AXI_WSTRB[ik])
+ wstrb_count = wstrb_count + 1;
+ end
+ // }}}
+
+ // wr_w_byte_count : Count of active WSTRBs
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_w_byte_count <= 0;
+ else if (triggered && M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ wr_w_byte_count <= wr_w_byte_count
+ + { {(LGCNT-C_AXI_DATA_WIDTH/8-1){1'b0}}, wstrb_count };
+ end
+ // }}}
+
+ // wr_aw_outstanding, wr_aw_zero_outstanding: AWV && AWR - BV && BR
+ // {{{
+ initial wr_aw_outstanding = 0;
+ initial wr_aw_zero_outstanding = 1;
+ initial wr_aw_err = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ wr_aw_outstanding <= 0;
+ wr_aw_zero_outstanding <= 1;
+ wr_aw_err <= 0;
+ end else if (!wr_aw_err)
+ case ({ M_AXI_AWVALID && M_AXI_AWREADY,
+ M_AXI_BVALID && M_AXI_BREADY })
+ 2'b10: begin
+ { wr_aw_err, wr_aw_outstanding } <= wr_aw_outstanding + 1;
+ wr_aw_zero_outstanding <= 0;
+ end
+ 2'b01: begin
+ wr_aw_outstanding <= wr_aw_outstanding - 1;
+ wr_aw_zero_outstanding <= (wr_aw_outstanding <= 1);
+ end
+ default: begin end
+ endcase
+`ifdef FORMAL
+ always @(*)
+ assert(wr_aw_zero_outstanding == (wr_aw_outstanding == 0));
+`endif
+ // }}}
+
+ // wr_aw_max_outstanding : max of wr_aw_outstanding
+ // {{{
+ initial wr_aw_max_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_aw_max_outstanding <= 0;
+ else if (triggered && (wr_aw_max_outstanding < wr_aw_outstanding))
+ wr_aw_max_outstanding <= wr_aw_outstanding;
+ // }}}
+
+ // wr_w_outstanding, wr_w_zero_outstanding: WV & WR & WL - BV & BR
+ // {{{
+ initial wr_w_outstanding = 0;
+ initial wr_w_zero_outstanding = 1;
+ initial wr_w_err = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ wr_w_outstanding <= 0;
+ wr_w_zero_outstanding <= 1;
+ wr_w_err <= 0;
+ end else if (!wr_w_err)
+ case ({ M_AXI_WVALID && M_AXI_WREADY && M_AXI_WLAST,
+ M_AXI_BVALID && M_AXI_BREADY })
+ 2'b10: begin
+ { wr_w_err, wr_w_outstanding } <= wr_w_outstanding + 1;
+ wr_w_zero_outstanding <= 0;
+ end
+ 2'b01: begin
+ wr_w_outstanding <= wr_w_outstanding - 1;
+ wr_w_zero_outstanding <= (wr_w_outstanding <= 1);
+ end
+ default: begin end
+ endcase
+`ifdef FORMAL
+ always @(*)
+ assert(wr_w_zero_outstanding == (wr_w_outstanding == 0));
+`endif
+ // }}}
+
+ // wr_w_max_outstanding: max of wr_w_outstanding + wr_in_progress
+ // {{{
+ initial wr_w_max_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_w_max_outstanding <= 0;
+ else if (triggered)
+ begin
+ if (wr_w_outstanding + (wr_in_progress ? 1:0)
+ > wr_max_outstanding)
+ wr_w_max_outstanding <= wr_w_outstanding
+ + (wr_in_progress ? 1:0);
+ end
+ // }}}
+
+ // wr_now_outs*, wr_max_outs*: max of wr_w_outs* and wr_aw_outs*
+ // {{{
+ always @(*)
+ begin
+ wr_now_outstanding = 0;
+ wr_now_outstanding = wr_w_max_outstanding;
+ if (wr_aw_max_outstanding > wr_now_outstanding)
+ wr_now_outstanding = wr_aw_max_outstanding;
+ end
+
+ initial wr_max_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_max_outstanding <= 0;
+ else if (triggered)
+ begin
+ if (wr_now_outstanding > wr_max_outstanding)
+ wr_max_outstanding <= wr_now_outstanding;
+ end
+ // }}}
+
+ // wr_in_progress: Flag, true between WVALID and WV && WR && WLAST
+ // {{{
+ initial wr_in_progress = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ wr_in_progress <= 0;
+ else if (M_AXI_WVALID)
+ begin
+ if (M_AXI_WREADY && M_AXI_WLAST)
+ wr_in_progress <= 0;
+ else
+ wr_in_progress <= 1;
+ end
+ // }}}
+
+ // Orthogonal write statistics
+ // {{{
+ // Here's where we capture our orthogonal measures for the write
+ // channel. It's important that, for all of these counters, only
+ // one of them ever counts a given burst. Hence, our criteria are
+ // binned and orthogonalized below.
+ //
+ // AW_O W_O WIP AWV AWR WV WR BV BR
+ // 0 0 0 0 0 IDLE-CYCLE
+ //
+ // 1 1 0 SLOW-DATA
+ // 1 1 0 Write stall (#1)
+ // 0 1 1 0 Write stall (#2)
+ // 1 1 W-BEAT (Counted elsewhere)
+ //
+ // 0 0 0 1 1 0 Early write address
+ // 0 0 0 1 0 0 Write address stall
+ // 1 0 0 0 W-DATA-LAG
+ // 0 1 0 0 WR Early (AWR after WLAST)
+ // 0 1 0 Write data before AWR
+ // 0 0 1 0 Early write data stall
+ //
+ // 1 1 0 0 0 B - Lag count
+ // 1 1 0 0 1 0 B - stall count
+ // 1 1 0 0 1 1 B - End of burst
+ //
+ // 0 0 1 1 Early write beat (special)
+ // 1 1 AW-BURST (Counted elsewhere)
+ //
+ // (DRAFT) Single channel AWR orthogonal
+ // {{{
+ // AWR bursts (got that)
+ // AWR Cycles = (AWR latency) + (WR Beats) / (Throughput)
+ //
+ // AWR bias = (counts where W follows AW)
+ // - (counts where AW follows W)
+ // If (AWR bias > 0), then
+ // Write lag = (BLAG + AWR bias) / AWR beats
+ // Else if (AWR bias < 0) (WData before AWVALID), then
+ // Write lag = (BLAG - AWR bias) / AWR beats
+ // Write throughput = (WR BEATS + WR STALL + WR SLOW
+ // + AWR bias) / WR Beats
+ // }}}
+ // Skip the boring stuffs (if using VIM folding)
+ // {{{
+ initial wr_data_lag = 0;
+ initial wr_idle_cycles = 0;
+ initial wr_early_beat = 0;
+ initial wr_awr_early = 0;
+ initial wr_b_lag_count = 0;
+ initial wr_b_stall_count = 0;
+ initial wr_b_end_count = 0;
+ initial wr_slow_data = 0;
+ initial wr_stall = 0;
+ initial wr_early_beat = 0;
+ initial wr_data_lag = 0;
+ // initial wr_aw_burst = 0;
+ initial wr_addr_stall = 0;
+ initial wr_addr_lag = 0;
+ initial wr_early_stall = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ begin
+ wr_data_lag <= 0;
+ wr_idle_cycles <= 0;
+ wr_early_beat <= 0;
+ wr_awr_early <= 0;
+ wr_b_lag_count <= 0;
+ wr_b_stall_count <= 0;
+ wr_b_end_count <= 0;
+ wr_slow_data <= 0;
+ wr_stall <= 0;
+ wr_data_lag <= 0;
+ wr_addr_stall <= 0;
+ wr_addr_lag <= 0;
+ wr_early_stall <= 0;
+ end else if (triggered)
+ // }}}
+ casez({ !wr_aw_zero_outstanding, !wr_w_zero_outstanding,
+ wr_in_progress,
+ M_AXI_AWVALID, M_AXI_AWREADY,
+ M_AXI_WVALID, M_AXI_WREADY,
+ M_AXI_BVALID, M_AXI_BREADY })
+ 9'b0000?0???: wr_idle_cycles <= wr_idle_cycles + 1;
+ //
+// Throughput measures
+ 9'b1?1??0???: wr_slow_data <= wr_slow_data + 1;
+ 9'b1????10??: wr_stall <= wr_stall + 1; // Stall #1
+ 9'b0?1??10??: wr_stall <= wr_stall + 1; // Stall #2
+ //
+ 9'b0??0?11??: wr_early_beat<= wr_early_beat + 1; // Before AWV
+ // 9'b?????11??: wr_beat <= wr_beat + 1; // Elsewhere
+ //
+// Lag measures
+ 9'b000110???: wr_awr_early <= wr_awr_early + 1;
+ 9'b000100???: wr_addr_stall <= wr_addr_stall + 1;
+
+ 9'b100??0???: wr_data_lag <= wr_data_lag + 1;
+ 9'b010??0???: wr_addr_lag <= wr_addr_lag + 1;
+ 9'b0?1??0???: wr_addr_lag <= wr_addr_lag + 1;
+ 9'b0?0??10??: wr_early_stall<= wr_early_stall+ 1;
+
+ 9'b110??0?0?: wr_b_lag_count <= wr_b_lag_count + 1;
+ 9'b110??0?10: wr_b_stall_count <= wr_b_stall_count + 1;
+ 9'b110??0?11: wr_b_end_count <= wr_b_end_count + 1;
+ //
+ default: begin end
+ endcase
+ // }}}
+
+ // WR Bias: How far ahead of WVALID does AWVALID show up?
+ // {{{
+ initial wr_bias = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ wr_bias <= 0;
+ else if (triggered)
+ begin
+ if ((!wr_aw_zero_outstanding
+ || (M_AXI_AWVALID && (!M_AXI_AWREADY
+ || (!M_AXI_WVALID || !M_AXI_WREADY))))
+ && (wr_w_zero_outstanding && !wr_in_progress))
+ // Address precedes data
+ wr_bias <= wr_bias + 1;
+ else if ((wr_aw_zero_outstanding
+ && (!M_AXI_AWVALID || !M_AXI_ARREADY))
+ && ((M_AXI_WVALID && (!M_AXI_AWVALID
+ || (M_AXI_WREADY && !M_AXI_AWREADY)))
+ || !wr_w_zero_outstanding || wr_in_progress))
+ // Data precedes data
+ wr_bias <= wr_bias - 1;
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read statistics
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // rd_max_burst_size = max(ARLEN)
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_max_burst_size <= 0;
+ else if (triggered)
+ begin
+ if (M_AXI_ARVALID && M_AXI_ARLEN > rd_max_burst_size)
+ rd_max_burst_size <= M_AXI_ARLEN;
+ end
+ // }}}
+
+ // rd_burst_count : Count of RVALID && RREADY && RLAST
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_burst_count <= 0;
+ else if (triggered && M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST)
+ rd_burst_count <= rd_burst_count + 1;
+ // }}}
+
+ // rd_byte_count : Count of (ARLEN+1) << ARSIZE)
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_byte_count <= 0;
+ else if (triggered && (M_AXI_ARVALID && M_AXI_ARREADY))
+ rd_byte_count <= rd_byte_count
+ + (({ 24'h0, M_AXI_ARLEN} + 32'h1)<< M_AXI_ARSIZE);
+ // }}}
+
+ // rd_beat_count : Count of RVALID && RREADY
+ // {{{
+ initial rd_beat_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_beat_count <= 0;
+ else if (triggered && (M_AXI_RVALID && M_AXI_RREADY))
+ rd_beat_count <= rd_beat_count+ 1;
+ // }}}
+
+ // rd_outstanding_bursts : internal counter, ARV && ARR - RV && RR && RL
+ // {{{
+ initial rd_outstanding_bursts = 0;
+ initial rd_err = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rd_outstanding_bursts <= 0;
+ rd_err <= 0;
+ end else if (!rd_err)
+ case ({ M_AXI_ARVALID && M_AXI_ARREADY,
+ M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST})
+ 2'b10: { rd_err, rd_outstanding_bursts } <= rd_outstanding_bursts + 1;
+ 2'b01: rd_outstanding_bursts <= rd_outstanding_bursts - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // rd_max_outstanding_bursts :
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_max_outstanding_bursts <= 0;
+ else if (triggered)
+ begin
+ if (rd_outstanding_bursts > rd_max_outstanding_bursts)
+ rd_max_outstanding_bursts <= rd_outstanding_bursts;
+ end
+ // }}}
+
+ generate for(gk=0; gk < (1<<C_AXI_ID_WIDTH); gk=gk+1)
+ begin : PER_ID_READ_STATISTICS
+
+ // rd_outstanding_bursts_id[gk], rd_nonzero_outstanding_id[gk]
+ // {{{
+ initial rd_outstanding_bursts_id[gk] = 0;
+ initial rd_nonzero_outstanding_id[gk] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rd_outstanding_bursts_id[gk] <= 0;
+ rd_nonzero_outstanding_id[gk] <= 0;
+ end else case(
+ { M_AXI_ARVALID && M_AXI_ARREADY && (M_AXI_ARID == gk),
+ M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST
+ && (M_AXI_RID == gk) })
+ 2'b10: begin
+ rd_outstanding_bursts_id[gk]
+ <= rd_outstanding_bursts_id[gk] + 1;
+ rd_nonzero_outstanding_id[gk] <= 1'b1;
+ end
+ 2'b01: begin
+ rd_outstanding_bursts_id[gk]
+ <= rd_outstanding_bursts_id[gk] - 1;
+ rd_nonzero_outstanding_id[gk]
+ <= (rd_outstanding_bursts_id[gk] > 1);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // rd_bursts_in_flight : Are bursts in flight for this ID?
+ // {{{
+ initial rd_bursts_in_flight = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rd_bursts_in_flight[gk] <= 0;
+ else if (M_AXI_RVALID && M_AXI_RID == gk)
+ begin
+ if (M_AXI_RREADY && M_AXI_RLAST)
+ rd_bursts_in_flight[gk] <= 1'b0;
+ else
+ rd_bursts_in_flight[gk] <= 1'b1;
+ end
+ // }}}
+ end endgenerate
+
+ // rd_responding : How many ID's have bursts in flight at any time?
+ // {{{
+ always @(*)
+ begin
+ rd_total_in_flight = 0;
+ for(ik=0; ik<(1<<C_AXI_ID_WIDTH); ik=ik+1)
+ if (rd_bursts_in_flight[ik])
+ rd_total_in_flight = rd_total_in_flight + 1;
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!S_AXI_ARESETN || !triggered))
+ rd_responding <= 0;
+ else
+ rd_responding <= rd_total_in_flight;
+ // }}}
+
+ // rd_max_responding_bursts : Max(bursts outstanding at any time)
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_max_responding_bursts <= 0;
+ else if (triggered)
+ begin
+ if (rd_responding > rd_max_responding_bursts)
+ rd_max_responding_bursts <= rd_responding;
+ end
+ // }}}
+
+
+ // rd_r_stalls : Count of RVALID && !RREADY
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_r_stalls <= 0;
+ else if (triggered && M_AXI_RVALID && !M_AXI_RREADY)
+ rd_r_stalls <= rd_r_stalls + 1;
+ // }}}
+
+ //
+ // Orthogonal read statistics
+ // {{{
+ // {{{
+ initial rd_idle_cycles = 0;
+ initial rd_lag_counter = 0;
+ initial rd_slow_link = 0;
+ initial rd_ar_stalls = 0;
+ initial rd_ar_cycles = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ begin
+ rd_idle_cycles <= 0;
+ rd_lag_counter <= 0;
+ rd_slow_link <= 0;
+ rd_ar_stalls <= 0;
+ rd_ar_cycles <= 0;
+ end else if (triggered)
+ begin
+ // }}}
+ if (!M_AXI_RVALID)
+ begin
+ if (rd_bursts_in_flight != 0)
+ rd_slow_link <= rd_slow_link + 1;
+ else if (rd_nonzero_outstanding_id != 0)
+ rd_lag_counter <= rd_lag_counter + 1;
+ else if (!M_AXI_ARVALID)
+ rd_idle_cycles <= rd_idle_cycles + 1;
+ else if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ rd_ar_stalls <= rd_ar_stalls + 1;
+ else // if M_AXI_ARVLD && M_AXI_ARRDY && otherwise idle
+ rd_ar_cycles <= rd_ar_cycles + 1;
+ end // else if (M_AXI_RREADDY) rd_beat_count <= rd_beat_count+1;
+ end
+ // }}}
+
+ // rd_first_lag
+ // {{{
+
+ // rd_first: are we responding to the first request from an idle bus?
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rd_first <= 1'b1;
+ else if (M_AXI_RVALID)
+ rd_first <= 1'b0;
+ else if (M_AXI_ARVALID && rd_nonzero_outstanding_id == 0)
+ rd_first <= 1'b1;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_request)
+ rd_first_lag <= 0;
+ else if (triggered && rd_first)
+ rd_first_lag <= rd_first_lag + 1;
+ // }}}
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Simulation report generation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Notes:
+ // {{{
+ // The following is an example report which can be to analyze bus
+ // statistics. It's divided in two parts. The first part prints out
+ // all the various collected data. All lines in this section are
+ // prefixed with "PERF:" to make them easier to identify via grep
+ // from a simulation output. The second half is designed to be
+ // cut/copy/pasted into a Matlab or Octave file. This contains
+ // the calculated data summaries for throughput, lag/latency, and
+ // efficiency. The performance monitor doesn't do the actual
+ // divide--but rather tells you what numbers need to be divided to
+ // achieve the desired performance measures.
+ // }}}
+ task report;
+ reg [31:0] num, dnm;
+
+ if (perf_err)
+ $display("PERF: BUS ERROR. INVALID RESULTS. RESET BUS TO CLEAR.");
+ else if (active_time[LGCNT])
+ $display("PERF: COUNTER OVERFLOW. TRY AGAIN.");
+ else if (wr_awburst_count == 0 && rd_burst_count == 0)
+ $display("PERF: NO DATA TRANSFERS RECORDED");
+ else begin
+ $display("PERF: AllBurstSiz\t0x%08x", { wr_max_outstanding,
+ rd_max_outstanding_bursts,
+ wr_max_burst_size,
+ rd_max_burst_size });
+ $display("PERF: TotalCycles\t0x%08x", active_time[LGCNT-1:0]);
+ $display("PERF: WrIdles\t\t0x%08x", wr_idle_cycles);
+ $display("PERF: AwrBursts\t\t0x%08x", wr_awburst_count);
+ $display("PERF: WrBeats\t\t0x%08x", wr_beat_count);
+ $display("PERF: AwrBytes\t\t0x%08x", wr_aw_byte_count);
+ $display("PERF: WrBytes\t\t0x%08x", wr_w_byte_count);
+ $display("PERF: WrSlowData\t0x%08x", wr_slow_data);
+ $display("PERF: WrStalls\t\t0x%08x", wr_stall);
+ $display("PERF: WrAddrLag\t\t0x%08x", wr_addr_lag);
+ $display("PERF: WrDataLag\t\t0x%08x", wr_data_lag);
+ $display("PERF: AwEarly\t\t0x%08x", wr_awr_early);
+ $display("PERF: WrEarlyData\t0x%08x", wr_early_beat);
+ $display("PERF: AwStall\t\t0x%08x", wr_addr_stall);
+ $display("PERF: EWrStalls\t\t0x%08x", wr_early_stall);
+ $display("PERF: WrBLags\t\t0x%08x", wr_b_lag_count);
+ $display("PERF: WrBStall\t\t0x%08x", wr_b_stall_count);
+ $display("PERF: WrBEnd\t\t0x%08x", wr_b_end_count);
+ $display("PERF: WrBias\t\t0x%08x", wr_bias);
+ //
+ $display("PERF: ---------------------------");
+ //
+ $display("PERF: RdIdles\t\t0x%08x", rd_idle_cycles);
+ $display("PERF: RdMaxB\t\t0x%08x", rd_max_responding_bursts);
+ $display("PERF: RdBursts\t\t0x%08x",rd_burst_count);
+ $display("PERF: RdBeats\t\t0x%08x", rd_beat_count);
+ $display("PERF: RdBytes\t\t0x%08x", rd_byte_count);
+ $display("PERF: ARCycles\t\t0x%08x",rd_ar_cycles);
+ $display("PERF: ArStalls\t\t0x%08x",rd_ar_stalls);
+ $display("PERF: RStalls\t\t0x%08x", rd_r_stalls);
+ $display("PERF: RdLag\t\t0x%08x", rd_lag_counter);
+ $display("PERF: RdSlow\t\t0x%08x", rd_slow_link);
+ //
+ $display("PERF: ---------------------------");
+ //
+ num = wr_awr_early + wr_addr_stall + wr_data_lag + wr_addr_lag
+ + wr_early_beat + wr_b_lag_count + wr_b_stall_count;
+ dnm = wr_awburst_count;
+ $display("perf_wrlag = %1d / %1d;", num, dnm);
+ num = wr_beat_count;
+ // dnm = wr_cycles;
+ dnm = active_time[LGCNT-1:0] - wr_b_end_count - wr_idle_cycles;
+ $display("perf_wreff = %1d / %1d;", num, dnm);
+ num = wr_beat_count;
+ dnm = wr_beat_count + wr_slow_data + wr_stall;
+ $display("perf_wrthruput = %1d / %1d;", num, dnm);
+
+ //
+ // Read lag = wait (ARSTALL + LAG) / # of Bursts
+ num = rd_ar_stalls + rd_lag_counter;
+ dnm = rd_burst_count;
+ $display("perf_rdlag = %1d / %1d;", num, dnm);
+ num = rd_first_lag;
+ dnm = rd_ar_cycles;
+ $display("perf_rdlatency = %1d / %1d;", num, dnm);
+ num = rd_beat_count;
+ dnm = rd_ar_cycles + rd_ar_stalls + rd_lag_counter + rd_beat_count
+ + rd_r_stalls + rd_slow_link;
+ $display("perf_rdeff = %1d / %1d;", num, dnm);
+ num = rd_beat_count;
+ dnm = rd_slow_link + rd_r_stalls + rd_beat_count;
+ $display("perf_rdthruput = %1d / %1d;", num, dnm);
+
+ end endtask
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT,
+ S_AXIL_ARADDR[ADDRLSB-1:0],
+ S_AXIL_AWADDR[ADDRLSB-1:0],
+ wskd_data, wskd_strb,
+ M_AXI_AWBURST, M_AXI_AWLOCK, M_AXI_AWCACHE, M_AXI_AWQOS,
+ M_AXI_AWID, M_AXI_AWADDR, M_AXI_ARADDR,
+ M_AXI_AWPROT, M_AXI_ARPROT,
+ M_AXI_BID, M_AXI_BRESP,
+ M_AXI_ARBURST, M_AXI_ARLOCK, M_AXI_ARCACHE, M_AXI_ARQOS,
+ M_AXI_WDATA, M_AXI_RDATA,
+ M_AXI_RRESP
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used in verfiying this core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_awr_outstanding== (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXIL_RVALID ? 1:0)
+ +(S_AXIL_ARREADY ? 0:1));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXIL_RVALID)
+ assert(S_AXIL_RDATA == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Start/stop requests
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(!start_request || !stop_request);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN && clear_request && !idle_bus))
+ assert(clear_request);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN && start_request
+ && (clear_request || !idle_bus)))
+ begin
+ if (!$past(axil_write_ready))
+ assert(start_request);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // While there are already cover properties in the formal property
+ // set above, you'll probably still want to cover something
+ // application specific here
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ assume(wr_aw_outstanding < 8'hff);
+
+ always @(*)
+ assume(wr_w_outstanding < 8'hff);
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axis2mm.v b/rtl/wb2axip/axis2mm.v
new file mode 100644
index 0000000..d578e41
--- /dev/null
+++ b/rtl/wb2axip/axis2mm.v
@@ -0,0 +1,2234 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axis2mm
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Converts an AXI-stream (input) to an AXI (full) memory
+// interface.
+//
+// {{{
+// While I am aware that other vendors sell similar components, if you
+// look under the hood you'll find no relation to anything but my own
+// work here.
+//
+// Registers:
+//
+// 0: CMD_CONTROL
+// Controls the transaction via either starting or aborting an
+// ongoing transaction, provides feedback regarding the current
+// status.
+//
+// [31] r_busy
+// True if the core is in the middle of a transaction. Set this
+// bit to one to begin a transaction.
+//
+// [30] r_err
+// True if the core has detected an error, such as FIFO
+// overflow while writing, or FIFO overflow in continuous mode.
+//
+// Writing a '1' to this bit while the core is idle will clear it.
+// New transfers will not start until this bit is cleared. For
+// this reason, I often start a new transfer by writing to bits
+// 31 and 30 of this register.
+//
+// _s2mm->a_control = 0xc0000000;
+//
+// Other bits may be appropriate as well, as discussed below,
+// depending on your application.
+//
+// [29] r_complete
+// True if the transaction has completed, whether normally or
+// abnormally (error or abort).
+//
+// Any write to the CMD_CONTROL register will clear this flag.
+//
+// [28] r_continuous
+// Normally the FIFO gets cleared and reset between operations.
+// However, if you set r_continuous, the core will then expectt
+// a second operation to take place following the first one.
+// In this case, the FIFO doesn't get cleared. However, if the
+// FIFO fills and the incoming data is both valid and changing,
+// the r_err flag will be set.
+//
+// Any write to the CMD_CONTROL register while the core is not
+// busy will adjust this bit.
+//
+// [27] !r_increment
+//
+// If clear, the core writes to subsequent and incrementing
+// addresses--the normal copy to memory case. If !r_increment is
+// set, the core writes to the same address throughout the
+// transaction. This is useful if you want to copy data to a
+// FIFO or other device living at a single address in the memory
+// map.
+//
+// Writes to CMD_CONTROL while the core is idle will adjust this
+// bit.
+//
+// [26] !tlast_syncd
+//
+// Read only status indicator. Reads 0 if OPT_TLAST_SYNC isn't
+// set. If OPT_TLAST_SYNC is set, then this bit indicates whether
+// or not the memory transfer is currently aligned with any stream
+// packets, or whether it is out of synchronization and waiting to
+// sync with the incoming stream. If the IP is out of alignment
+// and OPT_TLAST_SYNC is set, then the core will synchronize
+// itself automatically by holding TREADY high and ignoring data
+// until the first sample after TLAST.
+//
+// [25] Error code, decode error
+//
+// Read only bit. True following any AXI decode error. This will
+// also set the error bit. When the error bit is cleared, this
+// bit will be automatically cleared as well.
+//
+// [24] Error code, slave error
+//
+// Read only bit. True following any AXI slave error. This will
+// also set the error bit. When the error bit is cleared, this bit
+// will be automatically cleared as well.
+//
+// [23] Error code, overflow error
+//
+// Read only bit. True following any AXI stream overflow. As with
+// the other two error code bits, this one will also set the error
+// bit. It will also be cleared whenever the error bit is cleared.
+//
+// A "proper" AXI stream will never nor can it ever overflow. This
+// overflow check therefore looks for AXI stream protocol
+// violations. Such a violation might be not maintaining TVALID
+// when !TREADY, or changing TDATA when TVALID && !TREADY.
+// Likewise, if TLAST changes while TVALID && !TREADY an overflow
+// condition will be generated--but in this case only if the
+// OPT_TLAST_SYNC option is set.
+//
+// [22] Abort in progress
+//
+// Read only bit. This bit will be true following any abort until
+// the bus transactions complete. Self-clearing.
+//
+// [20:16] LGFIFO
+// These are read-only bits, returning the size of the FIFO.
+//
+// ABORT
+// If the core is busy, and the ABORT_KEY (currently set to 8'h26
+// below) is written to the top 8-bits ([31:24]) of this command
+// register, then the current transfer will be aborted. Yes, this
+// does repurpose the other bits written above. Any pending writes
+// will be completed, but nothing more will be written.
+//
+// Alternatively, the core will enter into an abort state
+// following any returned bus error indications.
+//
+// x4-c: (Unused and reserved)
+//
+// x10-14: CMD_ADDR
+// [C_AXI_ADDR_WIDTH-1:($clog2(C_AXI_DATA_WIDTH)-3)]
+//
+// If idle, this is address the core will write to when it starts.
+//
+// If busy, this is the address of either the current or next
+// address the core will request writing to.
+//
+// Upon completion, the address either returns to the starting
+// address (if r_continuous is clear), or otherwise becomes the
+// address where the core left off. In the case of an abort or an
+// error, this will be (near) the address that was last written.
+//
+// Why "near"? Because this address records the writes that have
+// been issued while no error is pending. If a bus error return
+// comes back, there may have been several writes issued before
+// that error address. Likewise if an overflow is detected, the
+// data associated with the overflow may have already been
+// somewhat written--the AXI bus doesn't stop on a dime.
+//
+// I hope to eventually add support for unaligned bursts. Such
+// support is not currently part of this core.
+//
+// x18-1c: CMD_LEN
+// [LGLEN-1:0]
+// The size of the transfer in bytes. Only accepts aligned
+// addresses, therefore bits [($clog2(C_AXI_DATA_WIDTH)-3):0]
+// will always be forced to zero. To find out what size bus
+// this core is conencted to, or the maximum transfer length,
+// write a -1 to this value and read the returning result.
+// Only the active bits will be set.
+//
+// While the core is busy, reads from this address will return
+// the number of items still to be written to the bus.
+//
+// }}}
+//
+// Status:
+// {{{
+// 1. The core passes both cover checks and formal property (assertion)
+// based checks. It has not (yet) been tested in real hardware.
+//
+// 2. I'd also like to support unaligned addresses (not lengths). This
+// will require aligning the data coming out of the FIFO as well.
+// As written, the core doesn't yet support these features.
+//
+// }}}
+//
+// Updates:
+// {{{
+// 20210426 - Fix. Upon reading, the current AXI write address and length
+// will (now) be returned. These are word address and lengths, not
+// packet address and lengths, and may (or may not) be aligned with
+// packet boundaries.
+//
+// In a similar fashion, the cmd_addr and cmd_length registers will
+// be adjusted on any error to (approximate) the address and length
+// remaining upon any discovered error.
+// }}}
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axis2mm #(
+ // {{{
+ //
+ // Downstream AXI (MM) address width. Remember, this is *byte*
+ // oriented, so an address width of 32 means this core can
+ // interact with a full 2^(C_AXI_ADDR_WIDTH) *bytes*.
+ parameter C_AXI_ADDR_WIDTH = 32,
+ // ... and the downstream AXI (MM) data width. High speed can
+ // be achieved by increasing this data width.
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXIS_TUSER_WIDTH = 0,
+ //
+ // OPT_AXIS_SKIDBUFFER will place a buffer between the incoming
+ // AXI stream interface and the outgoing AXI stream ready. This
+ // is technically necessary, and probably good practice too,
+ // when dealing with high speed networks.
+ parameter [0:0] OPT_AXIS_SKIDBUFFER = 1,
+ //
+ // OPT_AXIS_SKIDREGISTER will force the outputs of the skid
+ // buffer to be registered. This is something you would
+ // do primarily if you are trying to hit high speeds through
+ // this core.
+ parameter [0:0] OPT_AXIS_SKIDREGISTER = 0,
+ //
+ // OPT_TLAST_SYNC will synchronize the write with any incoming
+ // packets. Packets are assumed to be synchronized initially
+ // after any reset, or on the TVALID following any TLAST
+ parameter [0:0] OPT_TLAST_SYNC = 1,
+ //
+ // OPT_TREADY_WHILE_IDLE controls how the stream idle is set
+ // when the memory copy isn't running. If 1, then TREADY will
+ // be 1 and the core will ignore/throw out data when the core
+ // isn't busy. Otherwise, if this is set to 0, the core will
+ // force the stream to stall if ever no data is being copied.
+ parameter [0:0] OPT_TREADY_WHILE_IDLE = 1,
+ //
+ // If the ABORT_KEY is written to the upper 8-bits of the
+ // control/status word, the current operation will be halted.
+ // Any currently active (AxVALID through xVALID & xREADY)
+ // requests will continue to completion, and the core will then
+ // come to a halt.
+ parameter [7:0] ABORT_KEY = 8'h26,
+ //
+ // The size of the FIFO, log-based two. Hence LGFIFO=9 gives
+ // you a FIFO of size 2^(LGFIFO) or 512 elements. This is about
+ // as big as the FIFO should ever need to be, since AXI bursts
+ // can be 256 in length.
+ parameter LGFIFO = 9,
+ //
+ // Maximum number of bytes that can ever be transferred, in
+ // log-base 2. Hence LGLEN=20 will transfer 1MB of data.
+ parameter LGLEN = C_AXI_ADDR_WIDTH-1,
+ //
+ // We only ever use one AXI ID for all of our transactions.
+ // Here it is given as 0. Feel free to change it as necessary.
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_ID = 0,
+ //
+ // Set OPT_UNALIGNED to be able to transfer from unaligned
+ // addresses. Only applies to non fixed addresses and
+ // (possibly) non-continuous bursts. (THIS IS A PLACEHOLDER.
+ // UNALIGNED ADDRESSING IS NOT CURRENTLY SUPPORTED.)
+ localparam [0:0] OPT_UNALIGNED = 0,
+ //
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ parameter [0:0] OPT_CLKGATE = OPT_LOWPOWER,
+ //
+ // OPT_ASYNCMEM. The default FIFO implementation uses an
+ // asynchronous memory read, which will return the result in
+ // the same clock it is requested within. This forces the
+ // FIFO to use distributed RAM. For those architectures that
+ // don't have distributed RAM, or those designs that need to
+ // use block RAM, this flag should be set to zero.
+ parameter [0:0] OPT_ASYNCMEM = 1'b1,
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ localparam C_AXIL_ADDR_WIDTH = 5,
+ localparam C_AXIL_DATA_WIDTH = 32
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The stream interface
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire S_AXIS_TLAST,
+ input wire [((C_AXIS_TUSER_WIDTH>0) ? C_AXIS_TUSER_WIDTH-1:0):0]
+ S_AXIS_TUSER,
+ // }}}
+ //
+ // The control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ //
+
+ //
+ // The AXI (full) write interface
+ // {{{
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [7:0] M_AXI_AWLEN,
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ output wire [(C_AXIS_TUSER_WIDTH>0 ? C_AXIS_TUSER_WIDTH-1:0):0]
+ M_AXI_WUSER,
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ // }}}
+ //
+ //
+ // Create an output signal to indicate that we've finished
+ output reg o_int
+ // }}}
+ );
+
+ // Local parameters
+ // {{{
+ localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3;
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [2:0] CMD_CONTROL = 3'b000,
+ // CMD_UNUSED_1 = 3'b001,
+ // CMD_UNUSED_2 = 3'b010,
+ // CMD_UNUSED_3 = 3'b011,
+ CMD_ADDRLO = 3'b100,
+ CMD_ADDRHI = 3'b101,
+ CMD_LENLO = 3'b110,
+ CMD_LENHI = 3'b111;
+ // CMD_RESERVED = 2'b11;
+
+ // The maximum burst size is either 256, or half the FIFO size,
+ // whichever is smaller.
+ localparam TMP_LGMAXBURST=(LGFIFO > 8) ? 8 : LGFIFO-1;
+ // Of course, if this busts our 4kB packet size, it's an error.
+ // Let's clip to that size, then, if the LGMAXBURST would otherwise
+ // break it. So .. if 4kB is larger than our maximum burst size, then
+ // no change is required.
+ localparam LGMAXBURST = ((4096 / (C_AXI_DATA_WIDTH / 8))
+ > (1<<TMP_LGMAXBURST))
+ ? TMP_LGMAXBURST : $clog2(4096 * 8 / C_AXI_DATA_WIDTH);
+ localparam LGMAX_FIXED_BURST = (LGMAXBURST > 4) ? 4 : LGMAXBURST;
+ localparam MAX_FIXED_BURST = (1<<LGMAX_FIXED_BURST);
+ localparam LGLENW = LGLEN - ADDRLSB;
+ // localparam LGFIFOB = LGFIFO + ADDRLSB;
+ localparam ERRCODE_NOERR = 0,
+ ERRCODE_OVERFLOW = 0,
+ ERRCODE_SLVERR = 1,
+ ERRCODE_DECERR = 2;
+ // }}}
+
+ // Signal declarations
+ // {{{
+ wire clk_active, gated_clk;
+ wire i_clk = gated_clk;
+ wire i_reset = !S_AXI_ARESETN;
+
+ // Incoming stream buffer
+ wire sskd_valid, sskd_ready, sskd_last;
+ wire [C_AXI_DATA_WIDTH-1:0] sskd_data;
+ wire [((C_AXIS_TUSER_WIDTH>0) ? C_AXIS_TUSER_WIDTH : 1)-1:0] sskd_user;
+
+ reg r_busy, r_err, r_complete, r_continuous, r_increment,
+ cmd_abort, zero_length, r_pre_start,
+ w_cmd_start, w_complete, w_cmd_abort;
+ reg [2:0] r_errcode;
+ // reg cmd_start;
+ reg axi_abort_pending;
+
+ reg [LGLENW-1:0] aw_requests_remaining,
+ aw_bursts_outstanding,
+ aw_next_remaining;
+ reg [LGMAXBURST:0] wr_writes_pending;
+ reg [LGMAXBURST:0] r_max_burst;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_addr;
+
+ reg [C_AXI_ADDR_WIDTH-1:0] cmd_addr;
+ reg [LGLENW-1:0] cmd_length_w;
+
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, wide_length,
+ new_wideaddr, new_widelen,
+ wide_len_remaining,wide_current_address;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi,
+ new_lengthlo, new_lengthhi;
+
+ // FIFO signals
+ wire reset_fifo, write_to_fifo,
+ read_from_fifo;
+ wire [C_AXIS_TUSER_WIDTH+C_AXI_DATA_WIDTH-1:0] fifo_data;
+ wire [LGFIFO:0] fifo_fill;
+ wire fifo_full, fifo_empty;
+
+ wire awskd_valid, axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ //
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire arskd_valid, axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+ reg last_stalled, overflow, last_tlast;
+ reg [C_AXI_DATA_WIDTH-1:0] last_tdata;
+ reg [C_AXIL_DATA_WIDTH-1:0] w_status_word;
+
+ reg [LGLENW-1:0] r_remaining_w;
+
+ reg axi_awvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [7:0] axi_awlen;
+ reg axi_wvalid, axi_wlast;
+ reg [C_AXI_DATA_WIDTH/8-1:0] axi_wstrb;
+
+ // Speed up checking for zeros
+ reg aw_none_remaining,
+ aw_none_outstanding,
+ aw_last_outstanding,
+ wr_none_pending; // r_none_remaining;
+
+ reg w_phantom_start, phantom_start;
+ reg [LGMAXBURST:0] initial_burstlen;
+ reg [LGMAXBURST-1:0] addralign;
+
+ //
+ // Option processing
+ wire tlast_syncd;
+
+ reg aw_multiple_full_bursts,
+ aw_multiple_fixed_bursts,
+ aw_multiple_bursts_remaining,
+ aw_needs_alignment;
+ reg [LGFIFO:0] data_available;
+ reg sufficiently_filled;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Stream skidbuffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(OPT_AXIS_SKIDREGISTER),
+ .DW(C_AXI_DATA_WIDTH + 1
+ + ((C_AXIS_TUSER_WIDTH > 0) ? C_AXIS_TUSER_WIDTH:1)),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_PASSTHROUGH(!OPT_AXIS_SKIDBUFFER)
+ // }}}
+ ) skd_stream(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(reset_fifo),
+ .i_valid(S_AXIS_TVALID), .o_ready(S_AXIS_TREADY),
+ .i_data({ S_AXIS_TUSER, S_AXIS_TDATA, S_AXIS_TLAST }),
+ .o_valid(sskd_valid), .i_ready(sskd_ready),
+ .o_data({ sskd_user, sskd_data, sskd_last })
+ // }}}
+ );
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // This is mostly the skidbuffer logic, and handling of the VALID
+ // and READY signals for the AXI-lite control logic in the next
+ // section.
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilwskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ assign axil_write_ready = clk_active && awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilarskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr)
+ // }}}
+ );
+
+ assign axil_read_ready = clk_active && arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite controlled logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // last_stalled -- used in overflow checking
+ // {{{
+ initial last_stalled = 1'b0;
+ always @(posedge i_clk)
+ last_stalled <= (!i_reset) && (S_AXIS_TVALID && !S_AXIS_TREADY);
+ // }}}
+
+ // last_tlast -- used to check for protocol violations in overflow next
+ // {{{
+ always @(posedge i_clk)
+ if (!OPT_TLAST_SYNC)
+ last_tlast <= 0;
+ else
+ last_tlast <= S_AXIS_TLAST;
+ // }}}
+
+ // last_tdata -- used for overflow checking
+ // {{{
+ always @(posedge i_clk)
+ last_tdata <= S_AXIS_TDATA;
+ // }}}
+
+ // overflow
+ // {{{
+ // Capture and check whether or not the incoming data stream overflowed
+ // This is primarily a check for AXI-stream protocol violations, since
+ // you can't really overflow an AXI stream when following protocol. The
+ // problem is that many stream sources--such as ADCs for example--can't
+ // handle back-pressure. Hence, checking for stream violations can
+ // be used in those cases to check for overflows. The check is only
+ // so good, however, since an overflow condition might take place if
+ // an ADC produces two consecutive (identical) values, one of which gets
+ // skipped--and this check will not capture that.
+ initial overflow = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ overflow <= 0;
+ else if (last_stalled)
+ begin
+ // The overflow pulse is only one clock period long
+ overflow <= 0;
+ if (!sskd_valid)
+ overflow <= 1;
+ if (S_AXIS_TDATA != last_tdata)
+ overflow <= 1;
+ if (OPT_TLAST_SYNC && S_AXIS_TLAST != last_tlast)
+ overflow <= 1;
+
+ // This will be caught by r_err and r_continuous
+ end
+ // }}}
+
+ // w_cmd_abort, cmd_abort -- Abort transaction on user request
+ // {{{
+
+ // w_cmd_abort is a combinational value capturing a user abort request
+ // {{{
+ always @(*)
+ begin
+ w_cmd_abort = 0;
+ w_cmd_abort = (axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[31:24] == ABORT_KEY);
+ if (!r_busy)
+ w_cmd_abort = 0;
+ end
+ // }}}
+
+ // cmd_abort latches the user request until the abort is complete
+ // {{{
+ initial cmd_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cmd_abort <= 0;
+ else if (!r_busy)
+ cmd_abort <= 0;
+ else
+ cmd_abort <= cmd_abort || w_cmd_abort;
+ // }}}
+ // }}}
+
+ //
+ // Start command
+ //
+ always @(*)
+ if (r_busy)
+ w_cmd_start = 0;
+ else begin
+ w_cmd_start = 0;
+ if ((axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[31]))
+ w_cmd_start = 1;
+ if (r_err && !wskd_data[30])
+ w_cmd_start = 0;
+ if (zero_length)
+ w_cmd_start = 0;
+ end
+
+ // r_busy, r_complete -- Calculate busy or complete flags
+ // {{{
+ initial r_busy = 0;
+ initial r_complete = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_busy <= 0;
+ r_complete <= 0;
+ end else if (!r_busy)
+ begin
+ // Core is idle, waiting for a command to start
+ if (w_cmd_start)
+ r_busy <= 1'b1;
+
+ // Any write to the control register will clear the
+ // completion flag
+ if (axil_write_ready && awskd_addr == CMD_CONTROL)
+ r_complete <= 1'b0;
+ end else if (w_complete)
+ begin
+ // Clear busy once the transaction is complete
+ // This includes clearing busy on any error
+ r_complete <= 1;
+ r_busy <= 1'b0;
+ end
+ // }}}
+
+ // o_int -- interrupt generation
+ // {{{
+ initial o_int = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_int <= 0;
+ else
+ o_int <= (r_busy && w_complete)
+ || (r_continuous && overflow);
+ // }}}
+
+ // r_err, r_errcode: Error conditions checking
+ // {{{
+ initial r_err = 0;
+ initial r_errcode = ERRCODE_NOERR;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_err <= 0;
+ r_errcode <= ERRCODE_NOERR;
+ end else if (!r_busy)
+ begin
+ if (r_continuous && overflow)
+ begin
+ r_err <= 1;
+ r_errcode[ERRCODE_OVERFLOW] <= 1'b1;
+ end
+
+ if (axil_write_ready && awskd_addr == CMD_CONTROL
+ && wskd_strb[3] && wskd_data[30])
+ begin
+ r_err <= 0;
+ r_errcode <= ERRCODE_NOERR;
+ end
+ end else if (r_busy)
+ begin
+ if (M_AXI_BVALID && M_AXI_BREADY && M_AXI_BRESP[1])
+ begin
+ r_err <= 1'b1;
+ if (M_AXI_BRESP[0])
+ r_errcode[ERRCODE_DECERR] <= 1'b1;
+ else
+ r_errcode[ERRCODE_SLVERR] <= 1'b1;
+ end
+
+ if (overflow)
+ begin
+ r_err <= 1'b1;
+ r_errcode[ERRCODE_OVERFLOW] <= 1'b1;
+ end
+ end
+ // }}}
+
+ // r_continuous
+ // {{{
+ initial r_continuous = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_continuous <= 0;
+ else begin
+ if (r_continuous && overflow)
+ r_continuous <= 1'b0;
+ if (!r_busy && axil_write_ready && awskd_addr == CMD_CONTROL)
+ r_continuous <= wskd_strb[3] && wskd_data[28];
+ end
+ // }}}
+
+ // wide_*
+ // {{{
+ always @(*)
+ begin
+ wide_address = 0;
+ wide_address[C_AXI_ADDR_WIDTH-1:0] = cmd_addr;
+
+ wide_current_address = 0;
+ wide_current_address[C_AXI_ADDR_WIDTH-1:0] = axi_addr;
+
+ wide_length = 0;
+ wide_length[ADDRLSB +: LGLENW] = cmd_length_w;
+
+ wide_len_remaining = 0;
+ wide_len_remaining[ADDRLSB +: LGLENW] = r_remaining_w;
+ end
+ // }}}
+
+ // new_* wires created via apply_wstrb
+ // {{{
+ assign new_cmdaddrlo= apply_wstrb(
+ wide_address[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign new_cmdaddrhi=apply_wstrb(
+ wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ assign new_lengthlo= apply_wstrb(
+ wide_length[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign new_lengthhi= apply_wstrb(
+ wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ // }}}
+
+ // new_wideaddr, new_widelen
+ // {{{
+ // These are the wide_* adjusted for any write, and then adjusted again
+ // to make sure they are within the correct number of bits for the
+ // interface.
+ always @(*)
+ begin
+ new_wideaddr = wide_address;
+ if (awskd_addr == CMD_ADDRLO)
+ new_wideaddr[C_AXIL_DATA_WIDTH-1:0]
+ = new_cmdaddrlo;
+ if (awskd_addr == CMD_ADDRHI)
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]
+ = new_cmdaddrhi;
+ if (!OPT_UNALIGNED)
+ new_wideaddr[ADDRLSB-1:0] = 0;
+
+ // We only support C_AXI_ADDR_WIDTH address bits
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+
+ //
+ ///////////////
+ //
+
+ new_widelen = wide_length;
+ if (awskd_addr == CMD_LENLO)
+ new_widelen[C_AXIL_DATA_WIDTH-1:0] = new_lengthlo;
+ if (awskd_addr == CMD_LENHI)
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]
+ = new_lengthhi;
+
+ // We only support integer numbers of words--even if unaligned
+ new_widelen[ADDRLSB-1:0] = 0;
+
+ // We only support LGLEN length bits
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:LGLEN] = 0;
+ end
+ // }}}
+
+ // cmd_addr, cmd_length_w, r_increment, zero_length, aw_multiple_*
+ // {{{
+ initial r_increment = 1'b1;
+ initial cmd_addr = 0;
+ initial cmd_length_w = 0; // Counts in bytes
+ initial zero_length = 1;
+ initial aw_multiple_full_bursts = 0;
+ initial aw_multiple_fixed_bursts = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ // {{{
+ r_increment <= 1'b1;
+ cmd_addr <= 0;
+ cmd_length_w <= 0;
+ zero_length <= 1;
+ aw_multiple_full_bursts <= 0;
+ aw_multiple_fixed_bursts <= 0;
+ // }}}
+ end else if (axil_write_ready && !r_busy)
+ begin // Set the command, address, and length prior to operation
+ // {{{
+ case(awskd_addr)
+ CMD_CONTROL:
+ r_increment <= !wskd_data[27];
+ CMD_ADDRLO:
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ CMD_ADDRHI: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH)
+ begin
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ end
+ CMD_LENLO: begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ aw_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ aw_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+
+ end
+ CMD_LENHI: if (LGLEN > C_AXIL_DATA_WIDTH)
+ begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ aw_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ aw_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+ end
+ default: begin end
+ endcase
+ // }}}
+ end else if (r_busy)
+ begin // Updated cmd_addr && cmd_length during operation
+ // {{{
+ // Capture the last address written to in case of r_continuous
+ // (where we'll want to start again from this address),
+ // cmd_abort (where we'll want to know how far we got), or
+ // a bus error (where again we'll want to know how far we got)
+ if (r_continuous||axi_abort_pending)
+ cmd_addr <= axi_addr;
+
+ // Capture the number of remaining requests on either an error
+ // or an abort. Need to be careful here that we don't capture
+ // this address twice--hence the check for
+ // w_cmd_abort && !cmd_abort, and again for BVALID && !r_err
+ //
+ // Note that we can't check for axi_abort_pending here, since
+ // as soon as axi_abort_pending becomes true then cmd_length_w
+ // will get set to zero. Hence we need to capture this before
+ // axi_abort_pending gets set.
+ //
+ // Note that this is only an *approximate* length--especially
+ // in the case of either a bus error or an overflow, in which
+ // cases we won't really know what writes have been accomplished
+ // only that the last one failed. In that case, this will
+ // indicate the amount of writes we haven't requested
+ // (yet)--knowing that at least one (or more) of those prior
+ // must've failed. In the case of an overflow error, the
+ // overflow error may (or may not) have been written to memory
+ // by this time.
+ if (!axi_abort_pending && (cmd_abort || r_err
+ || (M_AXI_BVALID && M_AXI_BRESP[1])))
+ begin
+ cmd_length_w <= aw_requests_remaining;
+ zero_length <= (aw_requests_remaining == 0);
+ aw_multiple_full_bursts <= |aw_requests_remaining[LGLENW-1:LGMAXBURST];
+ aw_multiple_fixed_bursts <= |aw_requests_remaining[LGLENW-1:LGMAX_FIXED_BURST];
+ end
+
+ // Note that, because cmd_addr and cmd_length_w here aren't set
+ // on the same conditions that it is possible, on an error,
+ // that the two will not match.
+ // }}}
+ end
+ // }}}
+
+ // w_status_word
+ // {{{
+ always @(*)
+ begin
+ w_status_word = 0;
+
+ // The ABORT_KEY needs to be chosen so as not to look
+ // like these bits, lest someone read from the register
+ // and write back to it accidentally aborting any transaction
+ w_status_word[31] = r_busy;
+ w_status_word[30] = r_err;
+ w_status_word[29] = r_complete;
+ w_status_word[28] = r_continuous;
+ w_status_word[27] = !r_increment;
+ w_status_word[26] = !tlast_syncd;
+ w_status_word[25:23] = r_errcode;
+ w_status_word[22] = cmd_abort;
+ w_status_word[20:16] = LGFIFO;
+ end
+ // }}}
+
+ // axil_read_data
+ // {{{
+ always @(posedge i_clk)
+ if (!axil_read_valid || S_AXIL_RREADY)
+ begin
+ case(arskd_addr)
+ CMD_CONTROL: axil_read_data <= w_status_word;
+ CMD_ADDRLO: begin
+ if (!r_busy)
+ axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0];
+ else
+ axil_read_data <= wide_current_address[C_AXIL_DATA_WIDTH-1:0];
+ end
+ CMD_ADDRHI: begin
+ if (!r_busy)
+ axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ else
+ axil_read_data <= wide_current_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ end
+ CMD_LENLO: begin
+ if (!r_busy)
+ axil_read_data <= wide_length[C_AXIL_DATA_WIDTH-1:0];
+ else
+ axil_read_data <= wide_len_remaining[C_AXIL_DATA_WIDTH-1:0];
+ end
+ CMD_LENHI: begin
+ if (!r_busy)
+ axil_read_data <= wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ else
+ axil_read_data <= wide_len_remaining[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ end
+ default: axil_read_data <= 0;
+ endcase
+ end
+ // }}}
+
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The data FIFO section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Reset the FIFO between bursts, as long as r_continuous isn't set
+ assign reset_fifo = i_reset || (!r_busy && (!r_continuous || r_err));
+ assign write_to_fifo = sskd_valid && sskd_ready && tlast_syncd;
+ assign read_from_fifo = M_AXI_WVALID && M_AXI_WREADY
+ && !axi_abort_pending;
+
+ // We are ready if the FIFO isn't full and ...
+ // if OPT_TREADY_WHILE_IDLE is true
+ // at which point we ignore incoming data when we aren't
+ // busy, or
+ // if we aren't resetting the FIFO--that is, if data is actually
+ // going into the FIFO, or
+ // if we are ever out of synchronization--then we can ignore data
+ // until the next TLAST comes, where we must realign
+ // ourselves
+ assign sskd_ready = clk_active && !fifo_full
+ && (OPT_TREADY_WHILE_IDLE
+ || !reset_fifo || !tlast_syncd);
+
+ generate if (OPT_TLAST_SYNC)
+ begin : GEN_TLAST_SYNC
+ reg r_tlast_syncd;
+ // If the user has set OPT_TLAST_SYNC, then he wants to make
+ // certain that we don't start writing until the first stream
+ // value after the TLAST packet indicating an end of packet.
+ // For this cause, we'll maintain an r_tlast_syncd value
+ // indicating that the last value was a TLAST. If, at any
+ // time afterwards, a value is accepted into the stream but not
+ // into the FIFO, then the stream is now out of sync and
+ // r_tlast_syncd will drop.
+ //
+ // Note, this doesn't catch the case where the FIFO can't keep
+ // up. Lost data (might be) caught by overflow below.
+ initial r_tlast_syncd = 1;
+ always @(posedge i_clk)
+ if (!S_AXI_ARESETN)
+ r_tlast_syncd <= 1;
+ else if (sskd_valid && sskd_ready)
+ begin
+ if (sskd_last)
+ r_tlast_syncd <= 1;
+ else if (reset_fifo)
+ r_tlast_syncd <= 0;
+ end
+
+ assign tlast_syncd = r_tlast_syncd;
+ end else begin : NO_TLAST_SYNC
+
+ //
+ // If the option isn't set, then we are always synchronized.
+ //
+ assign tlast_syncd = 1;
+
+ // Verilator lint_off UNUSED
+ wire unused_tlast_sync;
+ assign unused_tlast_sync = &{ 1'b0, sskd_last };
+ // Verilator lint_on UNUSED
+ end endgenerate
+
+ // Incoming FIFO
+ // {{{
+ generate if (C_AXIS_TUSER_WIDTH > 0)
+ begin : FIFO_WITH_USER_DATA
+
+ sfifo #(
+ // {{{
+ .BW(C_AXIS_TUSER_WIDTH + C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO), .OPT_ASYNC_READ(OPT_ASYNCMEM)
+ // }}}
+ ) u_sfifo (
+ // {{{
+ .i_clk(i_clk), .i_reset(reset_fifo),
+ .i_wr(write_to_fifo),
+ .i_data({ sskd_user, sskd_data }),
+ .o_full(fifo_full), .o_fill(fifo_fill),
+ .i_rd(read_from_fifo), .o_data(fifo_data),
+ .o_empty(fifo_empty)
+ // }}}
+ );
+
+ assign { M_AXI_WUSER, M_AXI_WDATA } = fifo_data;
+
+ end else begin : NO_USER_DATA
+
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO), .OPT_ASYNC_READ(OPT_ASYNCMEM)
+ // }}}
+ ) u_sfifo (
+ // {{{
+ .i_clk(i_clk), .i_reset(reset_fifo),
+ .i_wr(write_to_fifo), .i_data(sskd_data),
+ .o_full(fifo_full), .o_fill(fifo_fill),
+ .i_rd(read_from_fifo), .o_data(fifo_data),
+ .o_empty(fifo_empty)
+ // }}}
+ );
+
+ assign M_AXI_WDATA = fifo_data;
+ assign M_AXI_WUSER = 0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_tuser;
+ assign unused_tuser = &{ 1'b0, sskd_user };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The outgoing AXI (full) protocol section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+
+ // Some counters to keep track of our state
+ // {{{
+
+
+ // Count the number of word writes left to be requested, starting
+ // with the overall command length and then reduced by M_AWLEN on
+ // each address write
+ // {{{
+ always @(*)
+ begin
+ // aw_next_remaining = aw_requests_remaining;
+ // if (phantom_start)
+ // aw_next_remaining = aw_requests_remaining
+ // + (~{{ (LGLENW-8){1'b0}}, M_AXI_AWLEN });
+ // // - (M_AXI_AWLEN+1)
+ // // + (~M_AXI_AWLEN+1) - 1
+ // // + ~M_AXI_AWLEN
+ aw_next_remaining = aw_requests_remaining
+ + { {(LGLENW-8){phantom_start}},
+ (phantom_start) ? ~M_AXI_AWLEN : 8'h00};
+ end
+
+ initial r_pre_start = 1;
+ always @(posedge i_clk)
+ if (!r_busy)
+ r_pre_start <= 1;
+ else
+ r_pre_start <= 0;
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ aw_needs_alignment <= 0;
+
+ if (|new_wideaddr[ADDRLSB +: LGMAXBURST])
+ begin
+ if (|new_widelen[LGLEN-1:(LGMAXBURST+ADDRLSB)])
+ aw_needs_alignment <= 1;
+ if (~new_wideaddr[ADDRLSB +: LGMAXBURST]
+ < new_widelen[ADDRLSB +: LGMAXBURST])
+ aw_needs_alignment <= 1;
+ end
+ end
+
+ initial aw_none_remaining = 1;
+ initial aw_requests_remaining = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ aw_requests_remaining <= cmd_length_w;
+ aw_none_remaining <= zero_length;
+ aw_multiple_bursts_remaining <= |cmd_length_w[LGLENW-1:LGMAXBURST+1];
+ end else if (cmd_abort || axi_abort_pending)
+ begin
+ aw_requests_remaining <= 0;
+ aw_none_remaining <= 1;
+ aw_multiple_bursts_remaining <= 0;
+ end else if (phantom_start)
+ begin
+ aw_requests_remaining <= aw_next_remaining;
+ aw_none_remaining<= !aw_multiple_bursts_remaining
+ &&(aw_next_remaining[LGMAXBURST:0] == 0);
+ aw_multiple_bursts_remaining
+ <= |aw_next_remaining[LGLENW-1:LGMAXBURST+1];
+ end
+ // }}}
+
+ // Calculate the maximum possible burst length, ignoring 4kB boundaries
+ // {{{
+ always @(*)
+ addralign = 1+(~cmd_addr[ADDRLSB +: LGMAXBURST]);
+
+ always @(*)
+ begin
+ initial_burstlen = (1<<LGMAXBURST);
+ if (!r_increment)
+ begin
+ initial_burstlen = MAX_FIXED_BURST;
+ if (!aw_multiple_fixed_bursts)
+ initial_burstlen = { 1'b0, cmd_length_w[LGMAXBURST-1:0] };
+ end else if (aw_needs_alignment)
+ initial_burstlen = { 1'b0, addralign };
+ else if (!aw_multiple_full_bursts)
+ initial_burstlen = { 1'b0, cmd_length_w[LGMAXBURST-1:0] };
+ end
+
+ initial r_max_burst = 0;
+ always @(posedge i_clk)
+ if (!r_busy || r_pre_start)
+ begin
+ // Force us to align ourself early
+ // That way we don't need to check for
+ // alignment (again) later
+ r_max_burst <= initial_burstlen;
+ end else if (phantom_start)
+ begin
+ // Verilator lint_off WIDTH
+ if (r_increment || LGMAXBURST <= LGMAX_FIXED_BURST)
+ begin
+ if (!aw_multiple_bursts_remaining
+ && aw_next_remaining[LGMAXBURST:0] < (1<<LGMAXBURST))
+ r_max_burst <= { 1'b0, aw_next_remaining[7:0] };
+ else
+ r_max_burst <= (1<<LGMAXBURST);
+ end else begin
+ if (!aw_multiple_bursts_remaining
+ && aw_next_remaining[LGMAXBURST:0] < MAX_FIXED_BURST)
+ r_max_burst <= { 1'b0, aw_next_remaining[7:0] };
+ else
+ r_max_burst <= MAX_FIXED_BURST;
+ end
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // Count the number of bursts outstanding--these are the number of
+ // AWVALIDs that have been accepted, but for which the BVALID has not
+ // (yet) been returned.
+ // {{{
+ initial aw_last_outstanding = 0;
+ initial aw_none_outstanding = 1;
+ initial aw_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ aw_bursts_outstanding <= 0;
+ aw_none_outstanding <= 1;
+ aw_last_outstanding <= 0;
+ end else case ({ phantom_start, M_AXI_BVALID && M_AXI_BREADY })
+ 2'b01: begin
+ aw_bursts_outstanding <= aw_bursts_outstanding - 1;
+ aw_none_outstanding <= (aw_bursts_outstanding == 1);
+ aw_last_outstanding <= (aw_bursts_outstanding == 2);
+ end
+ 2'b10: begin
+ aw_none_outstanding <= 0;
+ aw_bursts_outstanding <= aw_bursts_outstanding + 1;
+ aw_last_outstanding <= (aw_bursts_outstanding == 0);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // Are we there yet?
+ // {{{
+ // We can't just look for the last BVALID, since ... it might be
+ // possible to receive an abort before the FIFO is full enough to
+ // initiate the first burst.
+ always @(*)
+ if (!r_busy)
+ w_complete = 0;
+ else
+ w_complete = !M_AXI_AWVALID && (aw_none_remaining)
+ &&((aw_last_outstanding && M_AXI_BVALID)
+ || aw_none_outstanding);
+ // }}}
+
+ // Are we stopping early? Aborting something ongoing?
+ // {{{
+ initial axi_abort_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ axi_abort_pending <= 0;
+ else begin
+ if (M_AXI_BVALID && M_AXI_BREADY && M_AXI_BRESP[1])
+ axi_abort_pending <= 1;
+ if (cmd_abort)
+ axi_abort_pending <= 1;
+ if (r_err)
+ axi_abort_pending <= 1;
+ end
+ // }}}
+
+ // Count the number of WVALIDs yet to be sent on the write channel
+ // {{{
+ initial wr_none_pending = 1;
+ initial wr_writes_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ wr_writes_pending <= 0;
+ wr_none_pending <= 1;
+ end else case ({ phantom_start,
+ M_AXI_WVALID && M_AXI_WREADY })
+ 2'b00: begin end
+ 2'b01: begin
+ wr_writes_pending <= wr_writes_pending - 1;
+ wr_none_pending <= (wr_writes_pending == 1);
+ end
+ 2'b10: begin
+ wr_writes_pending <= wr_writes_pending + (M_AXI_AWLEN[LGMAXBURST-1:0] + 1);
+ wr_none_pending <= 0;
+ end
+ 2'b11: begin
+ wr_writes_pending <= wr_writes_pending + (M_AXI_AWLEN[LGMAXBURST-1:0]);
+ wr_none_pending <= (M_AXI_WLAST);
+ end
+ endcase
+ // }}}
+
+ // So that we can monitor where we are at, and perhaps restart it
+ // later, keep track of the current address used by the W-channel
+ // {{{
+ initial axi_addr = 0;
+ always @(posedge i_clk)
+ begin
+ if (!r_busy)
+ axi_addr <= cmd_addr;
+ else if (axi_abort_pending || !r_increment)
+ // Stop incrementing the address following an abort
+ axi_addr <= axi_addr;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ axi_addr <= axi_addr + (1<<ADDRLSB);
+
+ if (!OPT_UNALIGNED)
+ axi_addr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Count the number of words remaining to be written on the W channel
+ // {{{
+ // initial r_none_remaining = 1;
+ initial r_remaining_w = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_remaining_w <= 0;
+ // r_none_remaining <= 1;
+ end else if (!r_busy)
+ begin
+ r_remaining_w<= cmd_length_w;
+ // r_none_remaining <= zero_length;
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ r_remaining_w <= r_remaining_w - 1;
+ // r_none_remaining <= (r_remaining_w == 1);
+ end
+ // }}}
+
+ //
+ // }}}
+
+ // Phantom starts
+ // {{{
+ // Since we can't use the xREADY signals in our signaling, we hvae to
+ // be ready to generate both AWVALID and WVALID on the same cycle,
+ // and then hold AWVALID until it has been accepted. This means we
+ // can't use AWVALID as our burst start signal like we could in the
+ // slave. Instead, we'll use a "phantom" start signal. This signal
+ // is local here in our code. When this signal goes high, AWVALID
+ // and WVALID go high at the same time. Then, if AWREADY isn't held,
+ // we can still update all of our internal counters as though it were,
+ // based upon the phantom_start signal, and continue as though
+ // AWVALID were accepted on its first clock period.
+
+ always @(*)
+ begin
+ // We start again if there's more information to transfer
+ w_phantom_start = !aw_none_remaining;
+
+ // But not if the amount of information we need isn't (yet)
+ // in the FIFO.
+ if (!sufficiently_filled)
+ w_phantom_start = 0;
+
+ // Insist on a minimum of one clock between burst starts,
+ // since our burst length calculation takes a clock to do
+ if (phantom_start || r_pre_start)
+ w_phantom_start = 0;
+
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ w_phantom_start = 0;
+
+ // If we're still writing the last burst, then don't start
+ // any new ones
+ if (M_AXI_WVALID && (!M_AXI_WLAST || !M_AXI_WREADY))
+ w_phantom_start = 0;
+
+ // Finally, don't start any new bursts if we aren't already
+ // busy transmitting, or if we are in the process of aborting
+ // our transfer
+ if (!r_busy || cmd_abort || axi_abort_pending)
+ w_phantom_start = 0;
+ end
+
+ initial phantom_start = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ phantom_start <= 0;
+ else
+ phantom_start <= w_phantom_start;
+ // }}}
+
+
+ //
+ // WLAST
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ axi_wlast <= (cmd_length_w == 1);
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (w_phantom_start)
+ axi_wlast <= (r_max_burst == 1);
+ else if (phantom_start)
+ axi_wlast <= (M_AXI_AWLEN == 1);
+ else
+ axi_wlast <= (wr_writes_pending == 1 + (M_AXI_WVALID ? 1:0));
+ end
+ // }}}
+
+ // Calculate AWLEN and AWADDR for the next AWVALID
+ // {{{
+ //
+ initial data_available = 0;
+ always @(posedge i_clk)
+ if (reset_fifo)
+ data_available <= 0;
+ else if (axi_abort_pending)
+ data_available <= fifo_fill + (write_to_fifo ? 1:0);
+ else case({ write_to_fifo, phantom_start })
+ 2'b10: data_available <= data_available + 1;
+ // Verilator lint_off WIDTH
+ 2'b01: data_available <= data_available - (M_AXI_AWLEN+1);
+ 2'b11: data_available <= data_available - (M_AXI_AWLEN);
+ // Verilator lint_on WIDTH
+ default: begin end
+ endcase
+
+ always @(*)
+ if (|aw_requests_remaining[LGLENW-1:LGMAXBURST])
+ sufficiently_filled = |data_available[LGFIFO:LGMAXBURST];
+ else
+ sufficiently_filled = (data_available[LGMAXBURST-1:0]
+ >= aw_requests_remaining[LGMAXBURST-1:0]);
+
+ //
+ // axi_awlen
+ // {{{
+ generate if (LGMAXBURST >= 8)
+ begin : GEN_BIG_AWLEN
+
+ always @(posedge i_clk)
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awlen <= r_max_burst[7:0] - 8'd1;
+
+ end else begin : GEN_SHORT_AWLEN
+
+ always @(posedge i_clk)
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ axi_awlen <= { {(8-LGMAXBURST){1'b0}}, r_max_burst } - 8'd1;
+ axi_awlen[7:LGMAXBURST] <= 0;
+ end
+
+ end endgenerate
+ // }}}
+
+ always @(posedge i_clk)
+ begin
+ if (M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ axi_awaddr[ADDRLSB-1:0] <= 0;
+ // Verilator lint_off WIDTH
+ if (r_increment)
+ axi_awaddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ <= axi_awaddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + (M_AXI_AWLEN+1);
+ end
+ // Verilator lint_on WIDTH
+
+ if (!r_busy)
+ axi_awaddr<= cmd_addr;
+
+ if (!OPT_UNALIGNED)
+ axi_awaddr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // AWVALID
+ // {{{
+ initial axi_awvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awvalid <= w_phantom_start;
+ // }}}
+
+ // WVALID
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_wvalid <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (M_AXI_WVALID && !M_AXI_WLAST)
+ axi_wvalid <= 1;
+ else
+ axi_wvalid <= w_phantom_start;
+ end
+ // }}}
+
+ // axi_wstrb
+ // {{{
+ always @(posedge i_clk)
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wstrb <= (axi_abort_pending) ? 0:-1;
+ // }}}
+
+ // Fixed bus values
+ // {{{
+ assign M_AXI_AWVALID= axi_awvalid;
+ assign M_AXI_AWID = AXI_ID;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = axi_awlen;
+ // Verilator lint_off WIDTH
+ assign M_AXI_AWSIZE = $clog2(C_AXI_DATA_WIDTH)-3;
+ // Verilator lint_on WIDTH
+ assign M_AXI_AWBURST= { 1'b0, r_increment };
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE= 4'h3;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WSTRB = axi_wstrb;
+ assign M_AXI_WLAST = axi_wlast;
+ // M_AXI_WLAST = ??
+
+ assign M_AXI_BREADY = 1;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Optional) Clock gating
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_CLKGATE)
+ begin : CLK_GATING
+ // {{{
+ reg gatep, r_clk_active;
+ reg gaten /* verilator clock_enable */;
+
+ // clk_active
+ // {{{
+ initial r_clk_active = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clk_active <= 1'b1;
+ else begin
+ r_clk_active <= 1'b0;
+
+ if (r_busy)
+ r_clk_active <= 1'b1;
+ if (awskd_valid || wskd_valid || arskd_valid)
+ r_clk_active <= 1'b1;
+ if (S_AXIL_BVALID || S_AXIL_RVALID)
+ r_clk_active <= 1'b1;
+
+ // Activate the clock on incoming data
+ // reset_fifo = i_reset || (!r_busy && (!r_continuous || r_err));
+ // !reset_fifo= r_busy || (r_continuous && !r_err)
+ // !reset_fifo= (r_continuous && !r_err)
+ if (sskd_valid && !fifo_full
+ && (!tlast_syncd || (r_continuous && !r_err)))
+ r_clk_active <= 1'b1;
+ end
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // Gate the clock here locally
+ // {{{
+ initial gatep = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gatep <= 1'b1;
+ else
+ gatep <= clk_active;
+
+ initial gaten = 1'b1;
+ always @(negedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gaten <= 1'b1;
+ else
+ gaten <= gatep;
+
+ assign gated_clk = S_AXI_ACLK && gaten;
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // }}}
+ end else begin : NO_CLK_GATING
+ // {{{
+ // Always active
+ assign clk_active = 1'b1;
+ assign gated_clk = S_AXI_ACLK;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator coverage_off
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_BID,
+ M_AXI_BRESP[0], fifo_empty,
+ wr_none_pending, S_AXIL_ARADDR[AXILLSB-1:0],
+ S_AXIL_AWADDR[AXILLSB-1:0],
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_widelen };
+ // Verilator coverage_on
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The formal properties for this unit are maintained elsewhere.
+ // This core does, however, pass a full prove (w/ induction) for all
+ // bus properties.
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-stream data interface
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // (These are captured by the FIFO within)
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_AXIL_LGDEPTH = 4;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_rd_outstanding == (S_AXIL_RVALID ? 1:0)
+ +(S_AXIL_ARREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_WREADY ? 0:1));
+ assert(faxil_awr_outstanding== (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_AWREADY ? 0:1));
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI master memory interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // ...
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ //
+ .OPT_EXCLUSIVE(1'b0),
+ .OPT_NARROW_BURST(1'b0),
+ //
+ // ...
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awid( M_AXI_AWID),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awlen( M_AXI_AWLEN),
+ .i_axi_awsize( M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock( M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot( M_AXI_AWPROT),
+ .i_axi_awqos( M_AXI_AWQOS),
+ //
+ .i_axi_wvalid(M_AXI_WVALID),
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ .i_axi_wlast( M_AXI_WLAST),
+ //
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid( M_AXI_BID),
+ .i_axi_bresp( M_AXI_BRESP),
+ //
+ .i_axi_arvalid(1'b0),
+ .i_axi_arready(1'b0),
+ .i_axi_arid( M_AXI_AWID),
+ .i_axi_araddr( M_AXI_AWADDR),
+ .i_axi_arlen( M_AXI_AWLEN),
+ .i_axi_arsize( M_AXI_AWSIZE),
+ .i_axi_arburst(M_AXI_AWBURST),
+ .i_axi_arlock( M_AXI_AWLOCK),
+ .i_axi_arcache(M_AXI_AWCACHE),
+ .i_axi_arprot( M_AXI_AWPROT),
+ .i_axi_arqos( M_AXI_AWQOS),
+ //
+ .i_axi_rvalid(1'b0),
+ .i_axi_rready(1'b0),
+ .i_axi_rdata({(C_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_rlast(1'b0),
+ .i_axi_rresp(2'b00)
+ //
+ //
+ // }}}
+ );
+
+ //
+ // ...
+ //
+
+ always @(*)
+ assert(aw_bursts_outstanding
+ == faxi_awr_nbursts
+ + ((M_AXI_AWVALID&&!phantom_start) ? 1:0));
+
+ //
+ // ...
+ //
+
+ always @(posedge i_clk)
+ if (M_AXI_AWVALID)
+ begin
+ // ...
+ if (phantom_start)
+ begin
+ assert(wr_writes_pending == 0);
+ assert(wr_none_pending);
+ end else if ($past(phantom_start))
+ begin
+ assert(wr_writes_pending <= M_AXI_AWLEN+1);
+ end
+ end else begin
+ // ...
+ assert(wr_none_pending == (wr_writes_pending == 0));
+ end
+
+ always @(*)
+ if (r_busy && !OPT_UNALIGNED)
+ assert(M_AXI_AWADDR[ADDRLSB-1:0] == 0);
+
+ always @(*)
+ if (!OPT_UNALIGNED)
+ assert(cmd_addr[ADDRLSB-1:0] == 0);
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Other formal properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // ...
+ //
+
+ always @(*)
+ if (!r_busy)
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_BVALID);
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ assert(zero_length == (cmd_length_w == 0));
+
+ //
+ // ...
+ //
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(data_available >= (M_AXI_AWLEN+1));
+ end else if (M_AXI_AWVALID)
+ begin
+ assert(data_available <= (1<<LGFIFO));
+ end
+
+
+ always @(*)
+ if (phantom_start)
+ assert(wr_writes_pending == 0);
+
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(fifo_fill >= (M_AXI_AWLEN+1));
+ end else if (!axi_abort_pending)
+ assert(fifo_fill >= wr_writes_pending);
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!aw_none_remaining && !phantom_start)
+ begin
+ assert(aw_requests_remaining
+ + wr_writes_pending == r_remaining_w);
+
+ // Make sure we don't wrap
+ assert(wr_writes_pending <= r_remaining_w);
+ end else if (!aw_none_remaining)
+ begin
+ assert(aw_requests_remaining == r_remaining_w);
+
+ // Make sure we don't wrap
+ assert(wr_writes_pending == 0);
+ end
+ end else
+ assert(!M_AXI_WVALID);
+
+ always @(*)
+ assert(aw_none_remaining == (aw_requests_remaining == 0));
+
+ always @(*)
+ if (r_busy)
+ assert(aw_multiple_bursts_remaining == (|aw_requests_remaining[LGLENW-1:LGMAXBURST+1]));
+
+ always @(*)
+ begin
+ assert(aw_last_outstanding == (aw_bursts_outstanding == 1));
+ assert(aw_none_outstanding == (aw_bursts_outstanding == 0));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_complete)
+ assert(!r_busy);
+
+ always @(*)
+ assert(fifo_fill >= wr_writes_pending);
+
+ always @(*)
+ if (r_busy)
+ assert(r_max_burst <= (1<<LGMAXBURST));
+
+ always @(*)
+ if (r_busy && !r_pre_start)
+ assert((r_max_burst > 0) || (aw_requests_remaining == 0));
+
+
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(M_AXI_AWVALID && M_AXI_WVALID);
+ assert(wr_none_pending);
+ // assert(drain_triggered);
+ end
+
+ always @(posedge i_clk)
+ if (phantom_start)
+ begin
+ assert(r_max_burst > 0);
+ assert(M_AXI_AWLEN == $past(r_max_burst)-1);
+ end
+
+ always @(*)
+ if (r_busy && !r_err && !cmd_abort && aw_requests_remaining == f_length)
+ assert(initial_burstlen > 0);
+
+ always @(*)
+ if ((LGMAXBURST < 8) && (r_busy))
+ assert(M_AXI_AWLEN+1 <= (1<<LGMAXBURST));
+
+ always @(*)
+ if (r_busy && !r_increment && (M_AXI_AWVALID
+ || ((aw_requests_remaining < cmd_length_w)
+ && (aw_requests_remaining > 0))))
+ assert(M_AXI_AWLEN+1 <= MAX_FIXED_BURST);
+
+ always @(*)
+ if (M_AXI_AWVALID && M_AXI_AWADDR[ADDRLSB +: LGMAXBURST])
+ begin
+ // If we are ever unaligned, our first step should be to
+ // align ourselves
+ assert(M_AXI_AWLEN+1 <=
+ 1 +(~M_AXI_AWADDR[ADDRLSB +: LGMAXBURST]));
+ end
+
+ always @(*)
+ if (!wr_none_pending)
+ begin
+ // Second alignment check: every burst must end aligned
+ if (r_increment)
+ assert(axi_addr[ADDRLSB +: LGMAXBURST] + wr_writes_pending
+ <= (1<<LGMAXBURST));
+ end
+
+ always @(posedge i_clk)
+ if (phantom_start)
+ begin
+ assert(axi_awlen == $past(r_max_burst[7:0]) - 8'd1);
+ if (r_increment && (cmd_length_w > axi_awlen + 1)
+ &&(aw_requests_remaining != cmd_length_w))
+ assert(M_AXI_AWADDR[ADDRLSB +: LGMAXBURST] == 0);
+ end
+
+ //
+ // ...
+ //
+
+ // }}}
+
+ //
+ // Synchronization properties
+ // {{{
+ always @(*)
+ if (fifo_full || !clk_active)
+ begin
+ assert(!sskd_ready);
+ end else if (OPT_TREADY_WHILE_IDLE)
+ begin
+ // If we aren't full, and we set TREADY whenever idle,
+ // then we should otherwise have TREADY set at all times
+ assert(sskd_ready);
+ end else if (!tlast_syncd)
+ begin
+ // If we aren't syncd, always be ready until we finally sync up
+ assert(sskd_ready);
+ end else if (reset_fifo)
+ begin
+ // If we aren't accepting any data, but are idling with TREADY
+ // low, then make sure we drop TREADY when idle
+ assert(!sskd_ready);
+ end else
+ // In all other cases, assert TREADY
+ assert(sskd_ready);
+
+
+ //
+ // ...
+ //
+ // }}}
+
+ //
+ // Error logic checking
+ // {{{
+ always @(*)
+ if (!r_err)
+ begin
+ assert(r_errcode == 0);
+ end else
+ assert(r_errcode != 0);
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN) && $past(w_cmd_start)
+ &&!$past(overflow && r_continuous))
+ assert(!r_err);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // 1. All data values must get sent, and none skipped
+ // Captured in logic above, since M_AXI_WDATA is registered
+ // within the FIFO and not our interface
+ //
+ // 2. No addresses skipped.
+ // ...
+ //
+
+ // 3. If we aren't incrementing addresses, then our current address
+ // should always be the axi address
+ always @(*)
+ if (r_busy && !r_increment)
+ assert(axi_addr == cmd_addr);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg cvr_aborted, cvr_buserr, cvr_abort_clear;
+ reg [2:0] cvr_continued;
+
+ initial { cvr_aborted, cvr_buserr } = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ { cvr_aborted, cvr_buserr } <= 0;
+ else if (r_busy && !axi_abort_pending)
+ begin
+ if (cmd_abort && wr_writes_pending > 0)
+ cvr_aborted <= 1;
+ if (M_AXI_BVALID && M_AXI_BRESP[1])
+ cvr_buserr <= 1;
+ end
+
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_abort_clear <= 1'b0;
+ else if (cvr_aborted && !cvr_buserr && !cmd_abort)
+ begin
+ cvr_abort_clear <= 1;
+ end
+
+ always @(posedge i_clk)
+ if (!i_reset)
+ begin
+ cover(cvr_abort_clear);
+ end
+
+ initial cvr_continued = 0;
+ always @(posedge i_clk)
+ if (i_reset || r_err || cmd_abort)
+ cvr_continued <= 0;
+ else begin
+ // Cover a continued transaction across two separate bursts
+ if (r_busy && r_continuous)
+ cvr_continued[0] <= 1;
+ if (!r_busy && cvr_continued[0])
+ cvr_continued[1] <= 1;
+ if (r_busy && cvr_continued[1])
+ cvr_continued[2] <= 1;
+
+ //
+ // Artificially force us to look for two separate runs
+ if((!r_busy)&&($changed(cmd_length_w)))
+ cvr_continued <= 0;
+ end
+
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && !i_reset && $fell(r_busy))
+ begin
+ cover( r_err && cvr_aborted);
+ cover( r_err && cvr_buserr);
+ cover(!r_err);
+ if (!r_err && !axi_abort_pending && !cvr_aborted && !cvr_buserr)
+ begin
+ cover(cmd_length_w > 5);
+ cover(cmd_length_w > 8);
+ cover((cmd_length_w > 5)&&(cmd_addr[11:0] == 12'hff0));
+ cover(&cvr_continued && (cmd_length_w > 5));
+ end
+ end
+ // }}}
+
+ // This ends our formal property set
+`endif
+ // }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axisafety.v b/rtl/wb2axip/axisafety.v
new file mode 100644
index 0000000..ff7e351
--- /dev/null
+++ b/rtl/wb2axip/axisafety.v
@@ -0,0 +1,2339 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisafety.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Given that 1) AXI interfaces can be difficult to write and to
+// get right, 2) my experiences with the AXI infrastructure of an
+// ARM+FPGA is that the device needs a power cycle following a bus fault,
+// and given that I've now found multiple interfaces that had some bug
+// or other within them, the following is a bus fault isolator. It acts
+// as a bump in the log between the interconnect and the user core. As
+// such, it has two interfaces: The first is the slave interface,
+// coming from the interconnect. The second interface is the master
+// interface, proceeding to a slave that might be faulty.
+//
+// INTERCONNECT --> (S) AXISAFETY (M) --> POTENTIALLY FAULTY CORE
+//
+// The slave interface has been formally verified to be a valid AXI
+// slave, independent of whatever the potentially faulty core might do.
+// If the user core (i.e. the potentially faulty one) responds validly
+// to the requests of a master, then this core will turn into a simple
+// delay on the bus. If, on the other hand, the user core suffers from
+// a protocol error, then this core will set one of two output
+// flags--either o_write_fault or o_read_fault indicating whether a write
+// or a read fault was detected. Further attempts to access the user
+// core will result in bus errors, generated by the AXISAFETY core.
+//
+// Assuming the bus master can properly detect and deal with a bus error,
+// this should then make it possible to properly recover from a bus
+// protocol error without later needing to cycle power.
+//
+// The core does have a couple of limitations. For example, it can only
+// handle one burst transaction, and one ID at a time. This matches the
+// performances of Xilinx's example AXI full core, from which many user
+// cores have have been copied/modified from. Therefore, there will be
+// no performance loss for such a core.
+//
+// Because of this, it is still possible that the user core might have an
+// undetected fault when using this core. For example, if the interconnect
+// issues more than one bus request before receiving the response from the
+// first request, this safety core will stall the second request,
+// preventing the downstream core from seeing this second request. If the
+// downstream core would suffer from an error while handling the second
+// request, by preventing the user core from seeing the second request this
+// core eliminates that potential for error.
+//
+// Usage: The important part of using this core is to connect the slave
+// side to the interconnect, and the master side to the user core.
+//
+// Some options are available:
+//
+// 1) C_S_AXI_ADDR_WIDTH The number of address bits in the AXI channel
+// 1) C_S_AXI_DATA_WIDTH The number of data bits in the AXI channel
+// 3) C_S_AXI_ID_WIDTH The number of bits in an AXI ID. As currently
+// written, this number cannot be zero. You can, however, set it
+// to '1' and connect all of the relevant ID inputs to zero.
+//
+// These three parameters have currently been abbreviated with AW, DW, and
+// IW. I anticipate returning them to their original meanings, I just
+// haven't done so (yet).
+//
+// 4) OPT_TIMEOUT This is the number of clock periods, from
+// interconnect request to interconnect response, that the
+// slave needs to respond within. (The actual timeout from the
+// user slave's perspective will be shorter than this.)
+//
+// This timeout is part of the check that a slave core will
+// always return a response. From the standpoint of this core,
+// it can be set arbitrarily large. (From the standpoint of
+// formal verification, it needs to be kept short ...)
+// Feel free to set this even as large as 1ms if you would like.
+//
+// 5) OPT_SELF_RESET If set, will send a reset signal to the slave
+// following any write or read fault. This will cause the other
+// side of the link (write or read) to fault as well. Once the
+// channel then becomes inactive, the slave will be released from
+// reset and will be able to interact with the rest of the bus
+// again.
+//
+// 5) OPT_EXCLUSIVE If clear, will prohibit the slave from ever
+// receiving an exclusive access request. This saves the logic
+// in the firewall necessary to check that an EXOKAY response
+// to a write request was truly an allowed response. Since that
+// logic can explode with the ID width, sparing it can be quite
+// useful. If set, provides full exclusive access checking in
+// addition to the normal bus fault checking.
+//
+// Performance: As mentioned above, this core can handle one read burst and one
+// write burst at a time, no more. Further, the core will delay
+// an input path by one clock and the output path by another clock, so that
+// the latency involved with using this core is a minimum of two extra
+// clocks beyond the latency user slave core.
+//
+// Maximum write latency: N+3 clocks for a burst of N values
+// Maximum read latency: N+3 clocks for a burst of N values
+//
+// Faults detected:
+//
+// Write channel:
+// 1. Raising BVALID prior to the last write value being sent
+// to the user/slave core.
+// 2. Raising BVALID prior to accepting the write address
+// 3. A BID return ID that doesn't match the request AWID
+// 4. Sending too many returns, such as not dropping BVALID
+// or raising it when there is no outstanding write
+// request
+//
+// While the following technically isn't a violation of the
+// AXI protocol, it is treated as such by this fault isolator.
+//
+// 5. Accepting write data prior to the write address
+//
+// The fault isolator will guarantee that AWVALID is raised before
+// WVALID, so this shouldn't be a problem.
+//
+// Read channel:
+//
+// 1. Raising RVALID before accepting the read address, ARVALID
+// 2. A return ID that doesn't match the ID that was sent.
+// 3. Raising RVALID after the last return value, and so returning
+// too many response values
+// 4. Setting the RLAST value on anything but the last value from
+// the bus.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axisafety #(
+ // {{{
+ parameter C_S_AXI_ID_WIDTH = 1,
+ parameter C_S_AXI_DATA_WIDTH = 32,
+ parameter C_S_AXI_ADDR_WIDTH = 16,
+ // OPT_SELF_RESET: Set to true if the downstream slave should be
+ // reset upon detecting an error and separate from the upstream reset
+ // domain
+ parameter [0:0] OPT_SELF_RESET = 1'b0,
+ // OPT_EXCLUSIVE: Set to true if the downstream slave might possibly
+ // offer an exclusive access capability
+ parameter [0:0] OPT_EXCLUSIVE = 1'b0,
+ //
+ // I use the following abbreviations, IW, DW, and AW, to simplify
+ // the code below (and to help it fit within an 80 col terminal)
+ localparam IW = C_S_AXI_ID_WIDTH,
+ localparam DW = C_S_AXI_DATA_WIDTH,
+ localparam AW = C_S_AXI_ADDR_WIDTH,
+ //
+ // OPT_TIMEOUT references the number of clock cycles to wait
+ // between raising the *VALID signal and when the respective
+ // *VALID return signal must be high. You might wish to set this
+ // to a very high number, to allow your core to work its magic.
+ parameter OPT_TIMEOUT = 20
+ // }}}
+ ) (
+ // {{{
+ output reg o_read_fault,
+ output reg o_write_fault,
+ // User ports ends
+ // Do not modify the ports beyond this line
+
+ // Global Clock Signal
+ input wire S_AXI_ACLK,
+ // Global Reset Signal. This Signal is Active LOW
+ input wire S_AXI_ARESETN,
+ output reg M_AXI_ARESETN,
+ //
+ // The input side. This is where slave requests come into
+ // the core.
+ // {{{
+ //
+ // Write address
+ input wire [IW-1 : 0] S_AXI_AWID,
+ input wire [AW-1 : 0] S_AXI_AWADDR,
+ input wire [7 : 0] S_AXI_AWLEN,
+ input wire [2 : 0] S_AXI_AWSIZE,
+ input wire [1 : 0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [3 : 0] S_AXI_AWCACHE,
+ input wire [2 : 0] S_AXI_AWPROT,
+ input wire [3 : 0] S_AXI_AWQOS,
+ input wire S_AXI_AWVALID,
+ output reg S_AXI_AWREADY,
+ // Write data
+ input wire [DW-1 : 0] S_AXI_WDATA,
+ input wire [(DW/8)-1 : 0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ input wire S_AXI_WVALID,
+ output reg S_AXI_WREADY,
+ // Write return
+ output reg [IW-1 : 0] S_AXI_BID,
+ output reg [1 : 0] S_AXI_BRESP,
+ output reg S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ // Read address
+ input wire [IW-1 : 0] S_AXI_ARID,
+ input wire [AW-1 : 0] S_AXI_ARADDR,
+ input wire [7 : 0] S_AXI_ARLEN,
+ input wire [2 : 0] S_AXI_ARSIZE,
+ input wire [1 : 0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3 : 0] S_AXI_ARCACHE,
+ input wire [2 : 0] S_AXI_ARPROT,
+ input wire [3 : 0] S_AXI_ARQOS,
+ input wire S_AXI_ARVALID,
+ output reg S_AXI_ARREADY,
+ // Read data
+ output reg [IW-1 : 0] S_AXI_RID,
+ output reg [DW-1 : 0] S_AXI_RDATA,
+ output reg [1 : 0] S_AXI_RRESP,
+ output reg S_AXI_RLAST,
+ output reg S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ // }}}
+ //
+ // The output side, where slave requests are forwarded to the
+ // actual slave
+ // {{{
+ // Write address
+ // {{{
+ output reg [IW-1 : 0] M_AXI_AWID,
+ output reg [AW-1 : 0] M_AXI_AWADDR,
+ output reg [7 : 0] M_AXI_AWLEN,
+ output reg [2 : 0] M_AXI_AWSIZE,
+ output reg [1 : 0] M_AXI_AWBURST,
+ output reg M_AXI_AWLOCK,
+ output reg [3 : 0] M_AXI_AWCACHE,
+ output reg [2 : 0] M_AXI_AWPROT,
+ output reg [3 : 0] M_AXI_AWQOS,
+ output reg M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ // Write data
+ output reg [DW-1 : 0] M_AXI_WDATA,
+ output reg [(DW/8)-1 : 0] M_AXI_WSTRB,
+ output reg M_AXI_WLAST,
+ output reg M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ // Write return
+ input wire [IW-1 : 0] M_AXI_BID,
+ input wire [1 : 0] M_AXI_BRESP,
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ // }}}
+ // Read address
+ // {{{
+ output reg [IW-1 : 0] M_AXI_ARID,
+ output reg [AW-1 : 0] M_AXI_ARADDR,
+ output reg [7 : 0] M_AXI_ARLEN,
+ output reg [2 : 0] M_AXI_ARSIZE,
+ output reg [1 : 0] M_AXI_ARBURST,
+ output reg M_AXI_ARLOCK,
+ output reg [3 : 0] M_AXI_ARCACHE,
+ output reg [2 : 0] M_AXI_ARPROT,
+ output reg [3 : 0] M_AXI_ARQOS,
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ // Read data
+ input wire [IW-1 : 0] M_AXI_RID,
+ input wire [DW-1 : 0] M_AXI_RDATA,
+ input wire [1 : 0] M_AXI_RRESP,
+ input wire M_AXI_RLAST,
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ localparam LGTIMEOUT = $clog2(OPT_TIMEOUT+1);
+ localparam [1:0] OKAY = 2'b00, EXOKAY = 2'b01;
+ localparam SLAVE_ERROR = 2'b10;
+ //
+ //
+ // Register declarations
+ // {{{
+ reg faulty_write_return, faulty_read_return;
+ reg clear_fault;
+ //
+ // Timer/timeout variables
+ reg [LGTIMEOUT-1:0] write_timer, read_timer;
+ reg write_timeout, read_timeout;
+
+ //
+ // Double buffer the write address channel
+ reg r_awvalid, m_awvalid;
+ reg [IW-1:0] r_awid, m_awid;
+ reg [AW-1:0] r_awaddr, m_awaddr;
+ reg [7:0] r_awlen, m_awlen;
+ reg [2:0] r_awsize, m_awsize;
+ reg [1:0] r_awburst, m_awburst;
+ reg r_awlock, m_awlock;
+ reg [3:0] r_awcache, m_awcache;
+ reg [2:0] r_awprot, m_awprot;
+ reg [3:0] r_awqos, m_awqos;
+
+ //
+ // Double buffer for the write channel
+ reg r_wvalid,m_wvalid;
+ reg [DW-1:0] r_wdata, m_wdata;
+ reg [DW/8-1:0] r_wstrb, m_wstrb;
+ reg r_wlast, m_wlast;
+
+ //
+ // Double buffer the write response channel
+ reg m_bvalid; // r_bvalid == 0
+ reg [IW-1:0] m_bid;
+ reg [1:0] m_bresp;
+
+ //
+ // Double buffer the read address channel
+ reg r_arvalid, m_arvalid;
+ reg [IW-1:0] r_arid, m_arid;
+ reg [AW-1:0] r_araddr, m_araddr;
+ reg [7:0] r_arlen, m_arlen;
+ reg [2:0] r_arsize, m_arsize;
+ reg [1:0] r_arburst, m_arburst;
+ reg r_arlock, m_arlock;
+ reg [3:0] r_arcache, m_arcache;
+ reg [2:0] r_arprot, m_arprot;
+ reg [3:0] r_arqos, m_arqos;
+
+ //
+ // Double buffer the read data response channel
+ reg r_rvalid,m_rvalid;
+ reg [IW-1:0] m_rid;
+ reg [1:0] r_rresp, m_rresp;
+ reg m_rlast;
+ reg [DW-1:0] r_rdata, m_rdata;
+
+ //
+ // Write FIFO data
+ wire [IW-1:0] wfifo_id;
+ wire wfifo_lock;
+
+ //
+ // Read FIFO data
+ reg [IW-1:0] rfifo_id;
+ reg rfifo_lock;
+ reg [8:0] rfifo_counter;
+ reg rfifo_empty, rfifo_last, rfifo_penultimate;
+
+ //
+ //
+ reg [0:0] s_wbursts;
+ reg [8:0] m_wpending;
+ reg m_wempty, m_wlastctr;
+ wire waddr_valid, raddr_valid;
+
+ wire lock_enabled, lock_failed;
+ wire [(1<<IW)-1:0] lock_active;
+ reg rfifo_first, exread;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write channel processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Write address processing
+ // {{{
+ // S_AXI_AWREADY
+ // {{{
+ initial S_AXI_AWREADY = (OPT_SELF_RESET) ? 0:1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_AWREADY <= !OPT_SELF_RESET;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ S_AXI_AWREADY <= 0;
+ // else if (clear_fault)
+ // S_AXI_AWREADY <= 1;
+ else if (!S_AXI_AWREADY)
+ S_AXI_AWREADY <= S_AXI_BVALID && S_AXI_BREADY;
+ // }}}
+
+ generate if (OPT_EXCLUSIVE)
+ begin : GEN_LOCK_ENABLED
+ // {{{
+ reg r_lock_enabled, r_lock_failed;
+
+ // lock_enabled
+ // {{{
+ initial r_lock_enabled = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ r_lock_enabled <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_lock_enabled <= S_AXI_AWLOCK && lock_active[S_AXI_AWID];
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ r_lock_enabled <= 0;
+ // }}}
+
+ // lock_failed
+ // {{{
+ initial r_lock_failed = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ r_lock_failed <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_lock_failed <= S_AXI_AWLOCK && !lock_active[S_AXI_AWID];
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ r_lock_failed <= 0;
+ // }}}
+
+ assign lock_enabled = r_lock_enabled;
+ assign lock_failed = r_lock_failed;
+ // }}}
+ end else begin : NO_LOCK_ENABLE
+ // {{{
+ assign lock_enabled = 0;
+ assign lock_failed = 0;
+ // }}}
+ end endgenerate
+
+ // waddr_valid
+ // {{{
+ generate if (OPT_SELF_RESET)
+ begin : GEN_LCL_RESET
+ reg r_waddr_valid;
+
+ initial r_waddr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_waddr_valid <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_waddr_valid <= 1;
+ else if (waddr_valid)
+ r_waddr_valid <= !S_AXI_BVALID || !S_AXI_BREADY;
+
+ assign waddr_valid = r_waddr_valid;
+ end else begin : NO_LCL_RESET
+ assign waddr_valid = !S_AXI_AWREADY;
+ end endgenerate
+ // }}}
+
+ // r_aw*
+ // {{{
+ // Double buffer for the AW* channel
+ //
+ initial r_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (S_AXI_AWVALID && S_AXI_AWREADY)
+ begin
+ r_awvalid <= 1'b0;
+ r_awid <= S_AXI_AWID;
+ r_awaddr <= S_AXI_AWADDR;
+ r_awlen <= S_AXI_AWLEN;
+ r_awsize <= S_AXI_AWSIZE;
+ r_awburst <= S_AXI_AWBURST;
+ r_awlock <= S_AXI_AWLOCK;
+ r_awcache <= S_AXI_AWCACHE;
+ r_awprot <= S_AXI_AWPROT;
+ r_awqos <= S_AXI_AWQOS;
+ end else if (M_AXI_AWREADY)
+ r_awvalid <= 0;
+
+ if (!S_AXI_ARESETN)
+ begin
+ r_awvalid <= 0;
+ r_awlock <= 0;
+ end
+
+ if (!OPT_EXCLUSIVE)
+ r_awlock <= 1'b0;
+ end
+ // }}}
+
+ // m_aw*
+ // {{{
+ // Second half of the AW* double-buffer. The m_* terms reference
+ // either the value in the double buffer (assuming one is in there),
+ // or the incoming value (if the double buffer is empty)
+ //
+ always @(*)
+ if (r_awvalid)
+ begin
+ m_awvalid = r_awvalid;
+ m_awid = r_awid;
+ m_awaddr = r_awaddr;
+ m_awlen = r_awlen;
+ m_awsize = r_awsize;
+ m_awburst = r_awburst;
+ m_awlock = r_awlock;
+ m_awcache = r_awcache;
+ m_awprot = r_awprot;
+ m_awqos = r_awqos;
+ end else begin
+ m_awvalid = S_AXI_AWVALID && S_AXI_AWREADY;
+ m_awid = S_AXI_AWID;
+ m_awaddr = S_AXI_AWADDR;
+ m_awlen = S_AXI_AWLEN;
+ m_awsize = S_AXI_AWSIZE;
+ m_awburst = S_AXI_AWBURST;
+ m_awlock = S_AXI_AWLOCK && OPT_EXCLUSIVE;
+ m_awcache = S_AXI_AWCACHE;
+ m_awprot = S_AXI_AWPROT;
+ m_awqos = S_AXI_AWQOS;
+ end
+ // }}}
+
+ // M_AXI_AW*
+ // {{{
+ // Set the output AW* channel outputs--but only on no fault
+ //
+ initial M_AXI_AWVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+
+ if (o_write_fault)
+ M_AXI_AWVALID <= 0;
+ else
+ M_AXI_AWVALID <= m_awvalid;
+
+ M_AXI_AWID <= m_awid;
+ M_AXI_AWADDR <= m_awaddr;
+ M_AXI_AWLEN <= m_awlen;
+ M_AXI_AWSIZE <= m_awsize;
+ M_AXI_AWBURST <= m_awburst;
+ M_AXI_AWLOCK <= m_awlock && lock_active[m_awid];
+ M_AXI_AWCACHE <= m_awcache;
+ M_AXI_AWPROT <= m_awprot;
+ M_AXI_AWQOS <= m_awqos;
+ end
+
+ if (!OPT_EXCLUSIVE)
+ M_AXI_AWLOCK <= 0;
+ if (!M_AXI_ARESETN)
+ M_AXI_AWVALID <= 0;
+ end
+ // }}}
+ // }}}
+
+ // s_wbursts
+ // {{{
+ // Count write bursts outstanding from the standpoint of
+ // the incoming (slave) channel
+ //
+ // Notice that, as currently built, the count can only ever be one
+ // or zero.
+ //
+ // We'll use this count in a moment to determine if a response
+ // has taken too long, or if a response is returned when there's
+ // no outstanding request
+ initial s_wbursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_wbursts <= 0;
+ else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
+ s_wbursts <= 1;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ s_wbursts <= 0;
+ // }}}
+
+ // wfifo_id, wfifo_lock
+ // {{{
+ // Keep track of the ID of the last transaction. Since we only
+ // ever have one write transaction outstanding, this will need to be
+ // the ID of the returned value.
+
+ assign wfifo_id = r_awid;
+ assign wfifo_lock = r_awlock;
+ // }}}
+
+ // m_wpending, m_wempty, m_wlastctr
+ // {{{
+ // m_wpending counts the number of (remaining) write data values that
+ // need to be sent to the slave. It counts this number with respect
+ // to the *SLAVE*, not the master. When m_wpending == 1, WLAST shall
+ // be true. To make comparisons of (m_wpending == 0) or (m_wpending>0),
+ // m_wempty is assigned to (m_wpending). Similarly, m_wlastctr is
+ // assigned to (m_wpending == 1).
+ //
+ initial m_wpending = 0;
+ initial m_wempty = 1;
+ initial m_wlastctr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault)
+ begin
+ // {{{
+ m_wpending <= 0;
+ m_wempty <= 1;
+ m_wlastctr <= 0;
+ // }}}
+ end else if (M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ // {{{
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ // {{{
+ // Accepting AW* and W* packets on the same
+ // clock
+ if (m_wpending == 0)
+ begin
+ // The AW* and W* packets go together
+ m_wpending <= {1'b0, M_AXI_AWLEN};
+ m_wempty <= (M_AXI_AWLEN == 0);
+ m_wlastctr <= (M_AXI_AWLEN == 1);
+ end else begin
+ // The W* packet goes with the last
+ // AW* command, the AW* packet with a
+ // new one
+ m_wpending <= M_AXI_AWLEN+1;
+ m_wempty <= 0;
+ m_wlastctr <= (M_AXI_AWLEN == 0);
+ end
+ // }}}
+ end else begin
+ // {{{
+ m_wpending <= M_AXI_AWLEN+1;
+ m_wempty <= 0;
+ m_wlastctr <= (M_AXI_AWLEN == 0);
+ // }}}
+ end
+ // }}}
+ end else if (M_AXI_WVALID && M_AXI_WREADY && (!m_wempty))
+ begin
+ // {{{
+ // The AW* channel is idle, and we just accepted a value
+ // on the W* channel
+ m_wpending <= m_wpending - 1;
+ m_wempty <= (m_wpending <= 1);
+ m_wlastctr <= (m_wpending == 2);
+ // }}}
+ end
+ // }}}
+
+ // Write data processing
+ // {{{
+ // S_AXI_WREADY
+ // {{{
+ // The S_AXI_WREADY or write channel stall signal
+ //
+ // For this core, we idle at zero (stalled) until an AW* packet
+ // comes through
+ //
+ initial S_AXI_WREADY = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_WREADY <= 0;
+ else if (S_AXI_WVALID && S_AXI_WREADY)
+ begin
+ // {{{
+ if (S_AXI_WLAST)
+ S_AXI_WREADY <= 0;
+ else if (o_write_fault)
+ S_AXI_WREADY <= 1;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ S_AXI_WREADY <= 1;
+ else
+ S_AXI_WREADY <= 0;
+ // }}}
+ end else if ((s_wbursts == 0)&&(waddr_valid)
+ &&(o_write_fault || M_AXI_WREADY))
+ S_AXI_WREADY <= 1;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ S_AXI_WREADY <= 1;
+ // }}}
+
+ // r_w*
+ // {{{
+ // Double buffer for the write channel
+ //
+ // As before, the r_* values contain the values in the double
+ // buffer itself
+ //
+ initial r_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_wvalid <= 0;
+ else if (!r_wvalid)
+ begin
+ // {{{
+ r_wvalid <= (S_AXI_WVALID && S_AXI_WREADY
+ && M_AXI_WVALID && !M_AXI_WREADY);
+ r_wdata <= S_AXI_WDATA;
+ r_wstrb <= S_AXI_WSTRB;
+ r_wlast <= S_AXI_WLAST;
+ // }}}
+ end else if (o_write_fault || M_AXI_WREADY || !M_AXI_ARESETN)
+ r_wvalid <= 0;
+ // }}}
+
+ // m_w*
+ // {{{
+ // Here's the result of our double buffer
+ //
+ // m_* references the value within the double buffer in the case that
+ // something is in the double buffer. Otherwise, it is the values
+ // directly from the inputs. In the case of a fault, neither is true.
+ // Write faults override.
+ //
+ always @(*)
+ if (o_write_fault)
+ begin
+ // {{{
+ m_wvalid = !m_wempty;
+ m_wlast = m_wlastctr;
+ m_wstrb = 0;
+ // }}}
+ end else if (r_wvalid)
+ begin
+ // {{{
+ m_wvalid = 1;
+ m_wstrb = r_wstrb;
+ m_wlast = r_wlast;
+ // }}}
+ end else begin
+ // {{{
+ m_wvalid = S_AXI_WVALID && S_AXI_WREADY;
+ m_wstrb = S_AXI_WSTRB;
+ m_wlast = S_AXI_WLAST;
+ // }}}
+ end
+ // }}}
+
+ // m_wdata
+ // {{{
+ // The logic for the DATA output of the double buffer doesn't
+ // matter so much in the case of o_write_fault
+ always @(*)
+ if (r_wvalid)
+ m_wdata = r_wdata;
+ else
+ m_wdata = S_AXI_WDATA;
+ // }}}
+
+ // M_AXI_W*
+ // {{{
+ // Set the downstream write channel values
+ //
+ // As per AXI spec, these values *must* be registered. Note that our
+ // source here is the m_* double buffer/incoming write data switch.
+ initial M_AXI_WVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ M_AXI_WVALID <= m_wvalid;
+ M_AXI_WDATA <= m_wdata;
+ M_AXI_WSTRB <= m_wstrb;
+ if (OPT_EXCLUSIVE && lock_failed)
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= m_wlast;
+ end
+
+ // Override the WVALID signal (only) on reset, voiding any
+ // output we might otherwise send.
+ if (!M_AXI_ARESETN)
+ M_AXI_WVALID <= 0;
+ end
+ // }}}
+ // }}}
+
+ // Write fault detection
+ // {{{
+ // write_timer
+ // {{{
+ // The write timer
+ //
+ // The counter counts up to saturation. It is reset any time
+ // the write channel is either clear, or a value is accepted
+ // at the *MASTER* (not slave) side. Why the master side? Simply
+ // because it makes the proof below easier. (At one time I checked
+ // both, but then couldn't prove that the faults wouldn't get hit
+ // if the slave responded in time.)
+ initial write_timer = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !waddr_valid)
+ write_timer <= 0;
+ else if (!o_write_fault && M_AXI_BVALID)
+ write_timer <= 0;
+ else if (S_AXI_WREADY)
+ write_timer <= 0;
+ else if (!(&write_timer))
+ write_timer <= write_timer + 1;
+ // }}}
+
+ // write_timeout
+ // {{{
+ // Write timeout detector
+ //
+ // If the write_timer reaches the OPT_TIMEOUT, then the write_timeout
+ // will get set true. This will force a fault, taking the write
+ // channel off line.
+ initial write_timeout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ write_timeout <= 0;
+ else if (M_AXI_BVALID)
+ write_timeout <= write_timeout;
+ else if (S_AXI_WVALID && S_AXI_WREADY)
+ write_timeout <= write_timeout;
+ else if (write_timer >= OPT_TIMEOUT)
+ write_timeout <= 1;
+ // }}}
+
+ // faulty_write_return
+ // {{{
+ // fault_write_return
+ //
+ // This combinational logic is used to catch an invalid return, and
+ // so take the slave off-line before the return can corrupt the master
+ // channel. Reasons for taking the write return off line are listed
+ // below.
+ always @(*)
+ begin
+ faulty_write_return = 0;
+ if (M_AXI_WVALID && M_AXI_WREADY
+ && M_AXI_AWVALID && !M_AXI_AWREADY)
+ // Accepting the write *data* prior to the write
+ // *address* is a fault
+ faulty_write_return = 1;
+ if (M_AXI_BVALID)
+ begin
+ if (M_AXI_AWVALID || M_AXI_WVALID)
+ // Returning a B* acknowledgement while the
+ // request remains outstanding is also a fault
+ faulty_write_return = 1;
+ if (!m_wempty)
+ // Same as above, but this time the write
+ // channel is neither complete, nor is *WVALID
+ // active. Values remain to be written,
+ // and so a return is a fault.
+ faulty_write_return = 1;
+ if (s_wbursts <= (S_AXI_BVALID ? 1:0))
+ // Too many acknowledgments
+ //
+ // Returning more than one BVALID&BREADY for
+ // every AWVALID & AWREADY is a fault.
+ faulty_write_return = 1;
+ if (M_AXI_BID != wfifo_id)
+ // An attempt to return the wrong ID
+ faulty_write_return = 1;
+ if (M_AXI_BRESP == EXOKAY && (!OPT_EXCLUSIVE
+ || !lock_enabled))
+ // An attempt to return a valid lock, without
+ // a prior request
+ faulty_write_return = 1;
+ end
+ end
+ // }}}
+
+ // o_write_fault
+ // {{{
+ // On a write fault, we're going to disconnect the write port from
+ // the slave, and return errors on each write connect. o_write_fault
+ // is our signal determining if the write channel is disconnected.
+ //
+ // Most of this work is determined within faulty_write_return above.
+ // Here we do just a bit more:
+ initial o_write_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ o_write_fault <= 0;
+ else if ((!M_AXI_ARESETN&&o_read_fault) || write_timeout)
+ o_write_fault <= 1;
+ else if (M_AXI_BVALID && M_AXI_BREADY)
+ o_write_fault <= (o_write_fault) || faulty_write_return;
+ else if (M_AXI_WVALID && M_AXI_WREADY
+ && M_AXI_AWVALID && !M_AXI_AWREADY)
+ // Accepting the write data prior to the write address
+ // is a fault
+ o_write_fault <= 1;
+ // }}}
+ // }}}
+
+ // Write return generation
+ // {{{
+ // m_b*
+ // {{{
+ // Since we only ever allow a single write burst at a time, we don't
+ // need to double buffer the return channel. Hence, we'll set our
+ // return channel based upon the incoming values alone. Note that
+ // we're overriding the M_AXI_BID below, in order to make certain that
+ // the return goes to the right source.
+ //
+ always @(*)
+ if (o_write_fault)
+ begin
+ m_bvalid = (s_wbursts > (S_AXI_BVALID ? 1:0));
+ m_bid = wfifo_id;
+ m_bresp = SLAVE_ERROR;
+ end else begin
+ m_bvalid = M_AXI_BVALID;
+ if (faulty_write_return)
+ m_bvalid = 0;
+ m_bid = wfifo_id;
+ m_bresp = M_AXI_BRESP;
+ end
+ // }}}
+
+ // S_AXI_B*
+ // {{{
+ // We'll *never* stall the slaves BREADY channel
+ //
+ // If the slave returns the response we are expecting, then S_AXI_BVALID
+ // will be low and it can go directly into the S_AXI_BVALID slot. If
+ // on the other hand the slave returns M_AXI_BVALID at the wrong time,
+ // then we'll quietly accept it and send the write interface into
+ // fault detected mode, setting o_write_fault.
+ //
+ // Sadly, this will create a warning in Vivado. If/when you see it,
+ // see this note and then just ignore it.
+ assign M_AXI_BREADY = 1;
+
+ //
+ // Return a write acknowlegement at the end of every write
+ // burst--regardless of whether or not the slave does so
+ //
+ initial S_AXI_BVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_BVALID <= 0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ S_AXI_BVALID <= m_bvalid;
+
+ //
+ // Set the values associated with the response
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ S_AXI_BID <= m_bid;
+ S_AXI_BRESP <= m_bresp;
+ end
+ // }}}
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read channel processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Read address channel
+ // {{{
+
+ // S_AXI_ARREADY
+ // {{{
+ initial S_AXI_ARREADY = (OPT_SELF_RESET) ? 0:1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_ARREADY <= !OPT_SELF_RESET;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ S_AXI_ARREADY <= 0;
+ // else if (clear_fault)
+ // S_AXI_ARREADY <= 1;
+ else if (!S_AXI_ARREADY)
+ S_AXI_ARREADY <= (S_AXI_RVALID && S_AXI_RLAST && S_AXI_RREADY);
+ // }}}
+
+ // raddr_valid
+ // {{{
+ generate if (OPT_SELF_RESET)
+ begin : GEN_READ_RESET
+ assign raddr_valid = !rfifo_empty;
+ end else begin : SIMPLE_RADDR_VALID
+ assign raddr_valid = !S_AXI_ARREADY;
+ end endgenerate
+ // }}}
+
+ // r_ar*
+ // {{{
+ // Double buffer the values associated with any read address request
+ //
+ initial r_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ r_arvalid <= 0; // (M_AXI_ARVALID && !M_AXI_ARREADY);
+ r_arid <= S_AXI_ARID;
+ r_araddr <= S_AXI_ARADDR;
+ r_arlen <= S_AXI_ARLEN;
+ r_arsize <= S_AXI_ARSIZE;
+ r_arburst <= S_AXI_ARBURST;
+ r_arlock <= S_AXI_ARLOCK;
+ r_arcache <= S_AXI_ARCACHE;
+ r_arprot <= S_AXI_ARPROT;
+ r_arqos <= S_AXI_ARQOS;
+ end else if (M_AXI_ARREADY)
+ r_arvalid <= 0;
+
+ if (!M_AXI_ARESETN)
+ r_arvalid <= 0;
+ if (!OPT_EXCLUSIVE || !S_AXI_ARESETN)
+ r_arlock <= 1'b0;
+ end
+ // }}}
+
+ // m_ar*
+ // {{{
+ always @(*)
+ if (r_arvalid)
+ begin
+ m_arvalid = r_arvalid;
+ m_arid = r_arid;
+ m_araddr = r_araddr;
+ m_arlen = r_arlen;
+ m_arsize = r_arsize;
+ m_arburst = r_arburst;
+ m_arlock = r_arlock;
+ m_arcache = r_arcache;
+ m_arprot = r_arprot;
+ m_arqos = r_arqos;
+ end else begin
+ m_arvalid = S_AXI_ARVALID && S_AXI_ARREADY;
+ m_arid = S_AXI_ARID;
+ m_araddr = S_AXI_ARADDR;
+ m_arlen = S_AXI_ARLEN;
+ m_arsize = S_AXI_ARSIZE;
+ m_arburst = S_AXI_ARBURST;
+ m_arlock = S_AXI_ARLOCK && OPT_EXCLUSIVE;
+ m_arcache = S_AXI_ARCACHE;
+ m_arprot = S_AXI_ARPROT;
+ m_arqos = S_AXI_ARQOS;
+ end
+ // }}}
+
+ // M_AXI_AR*
+ // {{{
+ // Set the downstream values according to the transaction we've just
+ // received.
+ //
+ initial M_AXI_ARVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+
+ if (o_read_fault)
+ M_AXI_ARVALID <= 0;
+ else
+ M_AXI_ARVALID <= m_arvalid;
+
+ M_AXI_ARID <= m_arid;
+ M_AXI_ARADDR <= m_araddr;
+ M_AXI_ARLEN <= m_arlen;
+ M_AXI_ARSIZE <= m_arsize;
+ M_AXI_ARBURST <= m_arburst;
+ M_AXI_ARLOCK <= m_arlock && OPT_EXCLUSIVE;
+ M_AXI_ARCACHE <= m_arcache;
+ M_AXI_ARPROT <= m_arprot;
+ M_AXI_ARQOS <= m_arqos;
+ end
+
+ if (!M_AXI_ARESETN)
+ M_AXI_ARVALID <= 0;
+ end
+ // }}}
+
+ // }}}
+
+ //
+ // Read fault detection
+ // {{{
+
+ // read_timer
+ // {{{
+ // First step: a timer. The timer starts as soon as the S_AXI_ARVALID
+ // is accepted. Once that happens, rfifo_empty will no longer be true.
+ // The count is reset any time the slave produces a value for us to
+ // read. Once the count saturates, it stops counting.
+ initial read_timer = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ read_timer <= 0;
+ else if (rfifo_empty||(S_AXI_RVALID))
+ read_timer <= 0;
+ else if (M_AXI_RVALID)
+ read_timer <= 0;
+ else if (!(&read_timer))
+ read_timer <= read_timer + 1;
+ // }}}
+
+ // read_timeout
+ // {{{
+ // Once the counter > OPT_TIMEOUT, we have a timeout condition.
+ // We'll detect this one clock earlier below. If we ever enter
+ // a read time out condition, we'll set the read fault. The read
+ // timeout condition can only be cleared by a reset.
+ initial read_timeout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ read_timeout <= 0;
+ else if (rfifo_empty||S_AXI_RVALID)
+ read_timeout <= read_timeout;
+ else if (M_AXI_RVALID)
+ read_timeout <= read_timeout;
+ else if (read_timer == OPT_TIMEOUT)
+ read_timeout <= 1;
+ // }}}
+
+ //
+ // faulty_read_return
+ // {{{
+ // To avoid returning a fault from the slave, it is important to
+ // determine within a single clock period whether or not the slaves
+ // return value is at fault or not. That's the purpose of the code
+ // below.
+ always @(*)
+ begin
+ faulty_read_return = 0;
+ if (M_AXI_RVALID)
+ begin
+ if (M_AXI_ARVALID)
+ // It is a fault to return data before the
+ // request has been accepted
+ faulty_read_return = 1;
+ if (M_AXI_RID != rfifo_id)
+ // It is a fault to return data from a
+ // different ID
+ faulty_read_return = 1;
+ if (rfifo_last && (S_AXI_RVALID || !M_AXI_RLAST))
+ faulty_read_return = 1;
+ if (rfifo_penultimate && S_AXI_RVALID && (r_rvalid || !M_AXI_RLAST))
+ faulty_read_return = 1;
+ if (M_AXI_RRESP == EXOKAY && (!OPT_EXCLUSIVE || !rfifo_lock))
+ faulty_read_return = 1;
+ if (OPT_EXCLUSIVE && exread && M_AXI_RRESP == OKAY)
+ // Can't switch from EXOKAY to OKAY
+ faulty_read_return = 1;
+ if ((!OPT_EXCLUSIVE || (!rfifo_first && !exread))
+ && M_AXI_RRESP == EXOKAY)
+ // Can't switch from OKAY to EXOKAY
+ faulty_read_return = 1;
+ end
+ end
+ // }}}
+
+ // o_read_fault
+ // {{{
+ initial o_read_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ o_read_fault <= 0;
+ else if ((!M_AXI_ARESETN && o_write_fault) || read_timeout)
+ o_read_fault <= 1;
+ else if (M_AXI_RVALID)
+ begin
+ if (faulty_read_return)
+ o_read_fault <= 1;
+ if (!raddr_valid)
+ // It is a fault if we haven't issued an AR* request
+ // yet, and a value is returned
+ o_read_fault <= 1;
+ end
+ // }}}
+
+ // }}}
+
+ //
+ // Read return/acknowledgment processing
+ // {{{
+
+
+ // exread
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_ARESETN || !OPT_EXCLUSIVE)
+ exread <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ begin
+ if (!M_AXI_RRESP[1])
+ exread <= (M_AXI_RRESP == EXOKAY);
+ end else if (S_AXI_RVALID && S_AXI_RREADY)
+ begin
+ if (S_AXI_RLAST)
+ exread <= 1'b0;
+ end
+ // }}}
+
+ // r_rvalid
+ // {{{
+ // Step one, set/create the read return double buffer. If r_rvalid
+ // is true, there's a valid value in the double buffer location.
+ //
+ initial r_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault)
+ r_rvalid <= 0;
+ else if (!r_rvalid)
+ begin
+ // {{{
+ // Refuse to set the double-buffer valid on any fault.
+ // That will also keep (below) M_AXI_RREADY high--so that
+ // following the fault the slave might still (pretend) to
+ // respond properly
+ if (faulty_read_return)
+ r_rvalid <= 0;
+ else if (o_read_fault)
+ r_rvalid <= 0;
+ // If there's nothing in the double buffer, then we might
+ // place something in there. The double buffer only gets
+ // filled under two conditions: 1) something is pending to be
+ // returned, and 2) there's another return that we can't
+ // stall and so need to accept here.
+ r_rvalid <= M_AXI_RVALID && M_AXI_RREADY
+ && (S_AXI_RVALID && !S_AXI_RREADY);
+ // }}}
+ end else if (S_AXI_RREADY)
+ // Once the up-stream read-channel clears, we can always
+ // clear the double buffer
+ r_rvalid <= 0;
+ // }}}
+
+ // r_rresp, r_rdata
+ // {{{
+ // Here's the actual values kept whenever r_rvalid is true. This is
+ // the double buffer. Notice that r_rid and r_last are generated
+ // locally, so not recorded here.
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!r_rvalid)
+ begin
+ r_rresp <= M_AXI_RRESP;
+ r_rdata <= M_AXI_RDATA;
+ end
+ // }}}
+
+ // M_AXI_RREADY
+ // {{{
+ // Stall the downstream channel any time there's something in the
+ // double buffer. In spite of the ! here, this is a registered value.
+ assign M_AXI_RREADY = !r_rvalid;
+ // }}}
+
+ // rfifo_id, rfifo_lock
+ // {{{
+ // Copy the ID for later comparisons on the return
+ always @(*)
+ rfifo_id = r_arid;
+ always @(*)
+ rfifo_lock = r_arlock;
+ // }}}
+
+ // rfifo_[counter|empty|last|penultimate
+ // {{{
+ // Count the number of outstanding read elements. This is the number
+ // of read returns we still expect--from the upstream perspective. The
+ // downstream perspective will be off by both what's waiting for
+ // S_AXI_RREADY and what's in the double buffer
+ //
+ initial rfifo_counter = 0;
+ initial rfifo_empty = 1;
+ initial rfifo_last = 0;
+ initial rfifo_penultimate= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rfifo_counter <= 0;
+ rfifo_empty <= 1;
+ rfifo_last <= 0;
+ rfifo_penultimate<= 0;
+ end else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ rfifo_counter <= S_AXI_ARLEN+1;
+ rfifo_empty <= 0;
+ rfifo_last <= (S_AXI_ARLEN == 0);
+ rfifo_penultimate <= (S_AXI_ARLEN == 1);
+ end else if (!rfifo_empty)
+ begin
+ if (S_AXI_RVALID && S_AXI_RREADY)
+ begin
+ rfifo_counter <= rfifo_counter - 1;
+ rfifo_empty <= (rfifo_counter <= 1);
+ rfifo_last <= (rfifo_counter == 2);
+ rfifo_penultimate <= (rfifo_counter == 3);
+ end
+ end
+ // }}}
+
+ // lock_active
+ // {{{
+ generate if (OPT_EXCLUSIVE)
+ begin : CALC_LOCK_ACTIVE
+ // {{{
+ genvar gk;
+
+ for(gk=0; gk<(1<<IW); gk=gk+1)
+ begin : LOCK_PER_ID
+ reg r_lock_active;
+
+ initial r_lock_active = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN
+ || faulty_read_return || faulty_write_return)
+ r_lock_active <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY && rfifo_lock
+ && !S_AXI_ARREADY
+ && r_arid == gk[IW-1:0]
+ &&(!faulty_read_return && !o_read_fault)
+ && M_AXI_RRESP == EXOKAY)
+ r_lock_active <= 1;
+ else if (M_AXI_BVALID && M_AXI_BREADY && wfifo_lock
+ && r_awid == gk[IW-1:0]
+ && (S_AXI_ARREADY
+ || r_arid != gk[IW-1:0]
+ || !r_arlock)
+ && M_AXI_BRESP == OKAY)
+ r_lock_active <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLOCK
+ && S_AXI_ARID == gk[IW-1:0])
+ r_lock_active <= 0;
+
+ assign lock_active[gk] = r_lock_active;
+ end
+ // }}}
+ end else begin : LOCK_NEVER_ACTIVE
+ // {{{
+ assign lock_active = 0;
+
+ // Keep Verilator happy
+ // Verilator lint_off UNUSED
+ wire unused_lock;
+ assign unused_lock = &{ 1'b0, wfifo_lock };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+
+ // rfifo_first
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ rfifo_first <= 1'b0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ rfifo_first <= 1'b1;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ rfifo_first <= 1'b0;
+ else if (o_read_fault)
+ rfifo_first <= 1'b0;
+ // }}}
+
+ // m_rvalid, m_rresp
+ // {{{
+ // Determine values to send back and return
+ //
+ // This data set includes all the return values, even though only
+ // RRESP and RVALID are set in this block.
+ always @(*)
+ if (o_read_fault || (!M_AXI_ARESETN && OPT_SELF_RESET))
+ begin
+ m_rvalid = !rfifo_empty;
+ if (S_AXI_RVALID && rfifo_last)
+ m_rvalid = 0;
+ m_rresp = SLAVE_ERROR;
+ end else if (r_rvalid)
+ begin
+ m_rvalid = r_rvalid;
+ m_rresp = r_rresp;
+ end else begin
+ m_rvalid = M_AXI_RVALID && raddr_valid && !faulty_read_return;
+ m_rresp = M_AXI_RRESP;
+ end
+ // }}}
+
+ // m_rid
+ // {{{
+ // We've stored the ID locally, so that our response will never be in
+ // error
+ always @(*)
+ m_rid = rfifo_id;
+ // }}}
+
+ // m_rlast
+ // {{{
+ // Ideally, we'd want to say m_rlast = rfifo_last. However, we might
+ // have a value in S_AXI_RVALID already. In that case, the last
+ // value can only be true if we are one further away from the end.
+ // (Remember, rfifo_last and rfifo_penultimate are both dependent upon
+ // the *upstream* number of read values outstanding, not the downstream
+ // number which we can't trust in all cases)
+ always @(*)
+ if (S_AXI_RVALID)
+ m_rlast = rfifo_penultimate;
+ else
+ m_rlast = (rfifo_last);
+ // }}}
+
+ // m_rdata
+ // {{{
+ // In the case of a fault, rdata is a don't care. Therefore we can
+ // always set it based upon the values returned from the slave.
+ always @(*)
+ if (r_rvalid)
+ m_rdata = r_rdata;
+ else
+ m_rdata = M_AXI_RDATA;
+ // }}}
+
+ // S_AXI_R*
+ // {{{
+ // Record our return data values
+ //
+ // These are the values from either the slave, the double buffer,
+ // or an error value bypassing either as determined by the m_* values.
+ // Per spec, all of these values must be registered. First the valid
+ // signal
+ initial S_AXI_RVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_RVALID <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ S_AXI_RVALID <= m_rvalid;
+
+ // Then the various slave data channels
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ S_AXI_RID <= m_rid;
+ S_AXI_RRESP <= m_rresp;
+ S_AXI_RLAST <= m_rlast;
+ S_AXI_RDATA <= m_rdata;
+ end
+ // }}}
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Self-reset
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_SELF_RESET)
+ begin : GEN_SELFRESET
+ // {{{
+ // Declarations
+ // {{{
+ reg [4:0] reset_counter;
+ reg r_clear_fault, w_clear_fault;
+ wire reset_timeout;
+ // }}}
+
+ // M_AXI_ARESETN
+ // {{{
+ initial M_AXI_ARESETN = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_ARESETN <= 0;
+ else if (clear_fault)
+ M_AXI_ARESETN <= 1;
+ else if (o_read_fault || o_write_fault)
+ M_AXI_ARESETN <= 0;
+ // }}}
+
+ // reset_counter
+ // {{{
+ initial reset_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_ARESETN)
+ reset_counter <= 0;
+ else if (!reset_timeout)
+ reset_counter <= reset_counter+1;
+ // }}}
+
+ assign reset_timeout = reset_counter[4];
+
+ // r_clear_fault
+ // {{{
+ initial r_clear_fault <= 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clear_fault <= 1;
+ else if (!M_AXI_ARESETN && !reset_timeout)
+ r_clear_fault <= 0;
+ else if (!o_read_fault && !o_write_fault)
+ r_clear_fault <= 0;
+ else if (raddr_valid || waddr_valid)
+ r_clear_fault <= 0;
+ else
+ r_clear_fault <= 1;
+ // }}}
+
+ // clear_fault
+ // {{{
+ always @(*)
+ if (S_AXI_AWVALID || S_AXI_ARVALID)
+ w_clear_fault = 0;
+ else if (raddr_valid || waddr_valid)
+ w_clear_fault = 0;
+ else
+ w_clear_fault = r_clear_fault;
+
+ assign clear_fault = w_clear_fault;
+ // }}}
+
+ // }}}
+ end else begin : NO_SELFRESET
+ // {{{
+ always @(*)
+ M_AXI_ARESETN = S_AXI_ARESETN;
+ assign clear_fault = 0;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // {{{
+ // Below are just some of the formal properties used to verify
+ // this core. The full property set is maintained elsewhere.
+ //
+ parameter [0:0] F_OPT_FAULTLESS = 1;
+
+ //
+ // ...
+ // }}}
+
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_slave(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awid(S_AXI_AWID),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awlen(S_AXI_AWLEN),
+ .i_axi_awsize(S_AXI_AWSIZE),
+ .i_axi_awburst(S_AXI_AWBURST),
+ .i_axi_awlock(S_AXI_AWLOCK),
+ .i_axi_awcache(S_AXI_AWCACHE),
+ .i_axi_awprot(S_AXI_AWPROT),
+ .i_axi_awqos(S_AXI_AWQOS),
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ // }}}
+ // Write Data Channel
+ // {{{
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ .i_axi_wlast(S_AXI_WLAST),
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ // }}}
+ // Write response channel
+ // {{{
+ .i_axi_bid(S_AXI_BID),
+ .i_axi_bresp(S_AXI_BRESP),
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arid(S_AXI_ARID),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arlen(S_AXI_ARLEN),
+ .i_axi_arsize(S_AXI_ARSIZE),
+ .i_axi_arburst(S_AXI_ARBURST),
+ .i_axi_arlock(S_AXI_ARLOCK),
+ .i_axi_arcache(S_AXI_ARCACHE),
+ .i_axi_arprot(S_AXI_ARPROT),
+ .i_axi_arqos(S_AXI_ARQOS),
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rid(S_AXI_RID),
+ .i_axi_rdata(S_AXI_RDATA),
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rlast(S_AXI_RLAST),
+ // }}}
+ // Formal induction values
+ // {{{
+ .f_axi_awr_nbursts(f_axi_awr_nbursts),
+ .f_axi_wr_pending(f_axi_wr_pending),
+ .f_axi_rd_nbursts(f_axi_rd_nbursts),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding)
+ //
+ // ...
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // 1. We will only ever handle a single burst--never any more
+ always @(*)
+ assert(f_axi_awr_nbursts <= 1);
+
+ always @(*)
+ if (f_axi_awr_nbursts == 1)
+ begin
+ assert(!S_AXI_AWREADY);
+ if (S_AXI_BVALID)
+ assert(f_axi_wr_pending == 0);
+ if (!o_write_fault && M_AXI_AWVALID)
+ assert(f_axi_wr_pending == M_AXI_AWLEN + 1
+ - (M_AXI_WVALID ? 1:0)
+ - (r_wvalid ? 1 : 0));
+ if (!o_write_fault && f_axi_wr_pending > 0)
+ assert((!M_AXI_WVALID || !M_AXI_WLAST)
+ &&(!r_wvalid || !r_wlast));
+ if (!o_write_fault && M_AXI_WVALID && M_AXI_WLAST)
+ assert(!r_wvalid || !r_wlast);
+ end else begin
+ assert(s_wbursts == 0);
+ assert(!S_AXI_WREADY);
+ if (OPT_SELF_RESET)
+ begin
+ assert(1 || S_AXI_AWREADY || !M_AXI_ARESETN || !S_AXI_ARESETN);
+ end else
+ assert(S_AXI_AWREADY);
+ end
+
+ //
+ // ...
+ //
+
+ //
+ // AWREADY will always be low mid-burst
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(S_AXI_AWREADY == !OPT_SELF_RESET);
+ end else if (f_axi_wr_pending > 0)
+ begin
+ assert(!S_AXI_AWREADY);
+ end else if (s_wbursts)
+ begin
+ assert(!S_AXI_AWREADY);
+ end else if (!OPT_SELF_RESET)
+ begin
+ assert(S_AXI_AWREADY);
+ end else if (!o_write_fault && !o_read_fault)
+ assert(S_AXI_AWREADY || OPT_SELF_RESET);
+
+ always @(*)
+ if (f_axi_wr_pending > 0)
+ begin
+ assert(s_wbursts == 0);
+ end else if (f_axi_awr_nbursts > 0)
+ begin
+ assert((s_wbursts ? 1:0) == f_axi_awr_nbursts);
+ end else
+ assert(s_wbursts == 0);
+
+ always @(*)
+ if (!o_write_fault && S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+
+ always @(*)
+ if (M_AXI_ARESETN && (f_axi_awr_nbursts == 0))
+ begin
+ assert(o_write_fault || !M_AXI_AWVALID);
+ assert(!S_AXI_BVALID);
+ assert(s_wbursts == 0);
+ end else if (M_AXI_ARESETN && s_wbursts == 0)
+ assert(f_axi_wr_pending > 0);
+
+ always @(*)
+ if (S_AXI_WREADY)
+ begin
+ assert(waddr_valid);
+ end else if (f_axi_wr_pending > 0)
+ begin
+ if (!o_write_fault)
+ assert(M_AXI_WVALID && r_wvalid);
+ end
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(waddr_valid == !S_AXI_AWREADY);
+ end else begin
+ // ...
+ assert(waddr_valid == (f_axi_awr_nbursts > 0));
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault && f_axi_wr_pending > 0 && !S_AXI_WREADY)
+ assert(M_AXI_WVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault && m_wempty)
+ begin
+ assert(M_AXI_AWVALID || !M_AXI_WVALID);
+ assert(M_AXI_AWVALID || f_axi_wr_pending == 0);
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_AWVALID)
+ begin
+ if (!o_write_fault && !M_AXI_AWVALID)
+ begin
+ assert(f_axi_wr_pending + (M_AXI_WVALID ? 1:0) + (r_wvalid ? 1:0) == m_wpending);
+ end else if (!o_write_fault)
+ begin
+ assert(f_axi_wr_pending == M_AXI_AWLEN + 1 - (M_AXI_WVALID ? 1:0) - (r_wvalid ? 1:0));
+ assert(m_wpending == 0);
+ end
+ end
+
+ always @(*)
+ assert(m_wpending <= 9'h100);
+
+ always @(*)
+ if (M_AXI_ARESETN && (!o_write_fault)&&(f_axi_awr_nbursts == 0))
+ assert(!M_AXI_AWVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault)
+ begin
+ if (f_axi_awr_nbursts == 0)
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ end
+
+ if (!M_AXI_AWVALID)
+ assert(f_axi_wr_pending
+ +(M_AXI_WVALID ? 1:0)
+ + (r_wvalid ? 1:0)
+ == m_wpending);
+ if (f_axi_awr_nbursts == (S_AXI_BVALID ? 1:0))
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ end
+
+ if (S_AXI_BVALID)
+ assert(f_axi_awr_nbursts == 1);
+
+ if (M_AXI_AWVALID)
+ assert(m_wpending == 0);
+
+ if (S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+ end
+
+ always @(*)
+ assert(!r_awvalid);
+
+ always @(*)
+ assert(m_wempty == (m_wpending == 0));
+ always @(*)
+ assert(m_wlastctr == (m_wpending == 1));
+
+ //
+ // Verify the contents of the skid buffers
+ //
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (write_timer > OPT_TIMEOUT)
+ assert(o_write_fault || write_timeout);
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(waddr_valid == !S_AXI_AWREADY);
+ end else if (waddr_valid)
+ assert(!S_AXI_AWREADY);
+
+ always @(*)
+ if (!o_write_fault && M_AXI_ARESETN)
+ assert(waddr_valid == !S_AXI_AWREADY);
+
+ always @(*)
+ if (f_axi_wr_pending == 0)
+ assert(!S_AXI_WREADY);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ assert(f_axi_rd_nbursts <= 1);
+ always @(*)
+ assert(raddr_valid == (f_axi_rd_nbursts>0));
+ always @(*)
+ if (f_axi_rdid_nbursts > 0)
+ begin
+ assert(rfifo_id == f_axi_rd_checkid);
+ end else if (f_axi_rd_nbursts > 0)
+ assert(rfifo_id != f_axi_rd_checkid);
+
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ assert(raddr_valid);
+
+ always @(*)
+ if (raddr_valid)
+ assert(!S_AXI_ARREADY);
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(raddr_valid == !S_AXI_ARREADY);
+ end else begin
+ // ...
+ assert(raddr_valid == (f_axi_rd_nbursts > 0));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (f_axi_rd_outstanding == 0)
+ assert(!raddr_valid || OPT_SELF_RESET);
+
+ always @(*)
+ if (!o_read_fault && S_AXI_ARREADY)
+ assert(!M_AXI_ARVALID);
+
+ // Our "FIFO" counter
+ always @(*)
+ assert(rfifo_counter == f_axi_rd_outstanding);
+
+ always @(*)
+ begin
+ assert(rfifo_empty == (rfifo_counter == 0));
+ assert(rfifo_last == (rfifo_counter == 1));
+ assert(rfifo_penultimate == (rfifo_counter == 2));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_arvalid)
+ assert(skid_arvalid);
+
+ always @(*)
+ if (read_timer > OPT_TIMEOUT)
+ assert(read_timeout);
+
+
+ always @(posedge S_AXI_ACLK)
+ if (OPT_SELF_RESET && (!f_past_valid || !$past(M_AXI_ARESETN)))
+ begin
+ assume(!M_AXI_BVALID);
+ assume(!M_AXI_RVALID);
+ end
+
+ always @(*)
+ if (!o_read_fault && M_AXI_ARESETN)
+ assert(raddr_valid == !S_AXI_ARREADY);
+
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ assert(raddr_valid);
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_SELF_RESET)
+ begin
+ if (!M_AXI_ARESETN)
+ assert(o_read_fault || o_write_fault /* ... */ );
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Exclusive access property checking
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ...
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Good interface check
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (F_OPT_FAULTLESS)
+ begin : ASSUME_FAULTLESS
+ // {{{
+ //
+ // ...
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_master(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(M_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awid(M_AXI_AWID),
+ .i_axi_awaddr(M_AXI_AWADDR),
+ .i_axi_awlen(M_AXI_AWLEN),
+ .i_axi_awsize(M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock(M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot(M_AXI_AWPROT),
+ .i_axi_awqos(M_AXI_AWQOS),
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ // }}}
+ // Write Data Channel
+ // {{{
+ .i_axi_wvalid(M_AXI_WVALID),
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata(M_AXI_WDATA),
+ .i_axi_wstrb(M_AXI_WSTRB),
+ .i_axi_wlast(M_AXI_WLAST),
+ // }}}
+ // Write response channel
+ // {{{
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid(M_AXI_BID),
+ .i_axi_bresp(M_AXI_BRESP),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arid(M_AXI_ARID),
+ .i_axi_araddr(M_AXI_ARADDR),
+ .i_axi_arlen(M_AXI_ARLEN),
+ .i_axi_arsize(M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock(M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot(M_AXI_ARPROT),
+ .i_axi_arqos(M_AXI_ARQOS),
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rid(M_AXI_RID),
+ .i_axi_rdata(M_AXI_RDATA),
+ .i_axi_rresp(M_AXI_RRESP),
+ .i_axi_rlast(M_AXI_RLAST),
+ // }}}
+ // Formal outputs
+ // {{{
+ .f_axi_awr_nbursts(fm_axi_awr_nbursts),
+ .f_axi_wr_pending(fm_axi_wr_pending),
+ .f_axi_rd_nbursts(fm_axi_rd_nbursts),
+ .f_axi_rd_outstanding(fm_axi_rd_outstanding)
+ //
+ // ...
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Contract: If the downstream has no faults, then we
+ // shouldn't detect any here.
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!o_write_fault || !M_AXI_ARESETN);
+ end else
+ assert(!o_write_fault);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!o_read_fault || !M_AXI_ARESETN);
+ end else
+ assert(!o_read_fault);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!read_timeout || !M_AXI_ARESETN);
+ end else
+ assert(!read_timeout);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!write_timeout || !M_AXI_ARESETN);
+ end else
+ assert(!write_timeout);
+
+ always @(*)
+ if (S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (M_AXI_ARESETN && f_axi_rd_nbursts > 0)
+ assert(f_axi_rd_nbursts == fm_axi_rd_nbursts
+ + (M_AXI_ARVALID ? 1 : 0)
+ + (r_rvalid&&m_rlast ? 1 : 0)
+ + (S_AXI_RVALID&&S_AXI_RLAST ? 1 : 0));
+
+ always @(*)
+ if (M_AXI_ARESETN && f_axi_rd_outstanding > 0)
+ assert(f_axi_rd_outstanding == fm_axi_rd_outstanding
+ + (M_AXI_ARVALID ? M_AXI_ARLEN+1 : 0)
+ + (r_rvalid ? 1 : 0)
+ + (S_AXI_RVALID ? 1 : 0));
+
+ //
+ // ...
+ //
+ always @(*)
+ if (M_AXI_RVALID)
+ assert(!M_AXI_ARVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(m_wpending == fm_axi_wr_pending);
+
+ always @(*)
+ if (S_AXI_ARESETN && fm_axi_awr_nbursts > 0)
+ begin
+ assert(fm_axi_awr_nbursts== f_axi_awr_nbursts);
+ assert(fm_axi_awr_nbursts == 1);
+ //
+ // ...
+ //
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Exclusive address checking
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ end endgenerate
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Never path check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [C_S_AXI_ADDR_WIDTH-1:0] fc_never_write_addr,
+ fc_never_read_addr;
+ (* anyconst *) reg [C_S_AXI_DATA_WIDTH + C_S_AXI_DATA_WIDTH/8-1:0]
+ fc_never_write_data;
+ (* anyconst *) reg [C_S_AXI_DATA_WIDTH-1:0] fc_never_read_data;
+
+ // Write address
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_AWVALID)
+ assume(S_AXI_AWADDR != fc_never_write_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_write_fault && M_AXI_ARESETN && M_AXI_AWVALID)
+ assert(M_AXI_AWADDR != fc_never_write_addr);
+ // }}}
+
+ // Write data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_WVALID)
+ assume({ S_AXI_WDATA, S_AXI_WSTRB } != fc_never_write_data);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_write_fault && M_AXI_ARESETN && M_AXI_WVALID)
+ begin
+ if (lock_failed)
+ assert(M_AXI_WSTRB == 0);
+ else
+ assert({ M_AXI_WDATA, M_AXI_WSTRB } != fc_never_write_data);
+ end
+ // }}}
+
+ // Read address
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_ARVALID)
+ assume(S_AXI_ARADDR != fc_never_read_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && r_arvalid)
+ assume(r_araddr != fc_never_read_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_read_fault && M_AXI_ARESETN && M_AXI_ARVALID)
+ assert(M_AXI_ARADDR != fc_never_read_addr);
+ // }}}
+
+ // Read data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && M_AXI_RVALID)
+ assume(M_AXI_RDATA != fc_never_read_data);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_RVALID && S_AXI_RRESP != SLAVE_ERROR)
+ assert(S_AXI_RDATA != fc_never_read_data);
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // We'll use these to determine the best performance this core
+ // can achieve
+ //
+ reg [4:0] f_dbl_rd_count, f_dbl_wr_count;
+ reg [4:0] f_rd_count, f_wr_count;
+
+ // f_wr_count
+ // {{{
+ initial f_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault)
+ f_wr_count <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ begin
+ if (!(&f_wr_count))
+ f_wr_count <= f_wr_count + 1;
+ end
+ // }}}
+
+ // f_dbl_wr_count
+ // {{{
+ initial f_dbl_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault||(S_AXI_AWVALID && S_AXI_AWLEN < 3))
+ f_dbl_wr_count <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ begin
+ if (!(&f_dbl_wr_count))
+ f_dbl_wr_count <= f_dbl_wr_count + 1;
+ end
+ // }}}
+
+ // f_rd_count
+ // {{{
+ initial f_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault)
+ f_rd_count <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ begin
+ if (!(&f_rd_count))
+ f_rd_count <= f_rd_count + 1;
+ end
+ // }}}
+
+ // f_dbl_rd_count
+ // {{{
+ initial f_dbl_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault||(S_AXI_ARVALID && S_AXI_ARLEN < 3))
+ f_dbl_rd_count <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ begin
+ if (!(&f_dbl_rd_count))
+ f_dbl_rd_count <= f_dbl_rd_count + 1;
+ end
+ // }}}
+
+ // Can we complete a single write burst without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_wr_count > 1) && (!o_write_fault));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_wr_count > 1) && (!o_write_fault));
+ // }}}
+
+ // Can we complete four write bursts without fault? (and return to idle)
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_wr_count > 3) && (!o_write_fault)
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID
+ && !S_AXI_BVALID)
+ && (f_axi_awr_nbursts == 0)
+ && (f_axi_wr_pending == 0));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_wr_count > 3) && (!o_write_fault)
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID
+ && !S_AXI_BVALID)
+ && (f_axi_awr_nbursts == 0)
+ && (f_axi_wr_pending == 0));
+ // }}}
+
+ // Can we complete a single read burst without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_rd_count > 1) && (!o_read_fault));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_rd_count > 1) && (!o_read_fault));
+ // }}}
+
+ // Can we complete four read bursts without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_rd_count > 3) && (f_axi_rd_nbursts == 0)
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_rd_count > 3) && (f_axi_rd_nbursts == 0)
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axisbroadcast.v b/rtl/wb2axip/axisbroadcast.v
new file mode 100644
index 0000000..719108f
--- /dev/null
+++ b/rtl/wb2axip/axisbroadcast.v
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisbroadcast
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: AXI-Stream broadcaster: one slave input port gets broadcast to
+// multiple AXI-Stream master ports.
+//
+// This design does not explicitly implement TLAST, TUSER, TID, or any
+// other T* structure. These can be implemented by simply incorporating
+// them into TDATA.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2021-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axisbroadcast #(
+ parameter C_AXIS_DATA_WIDTH = 16,
+ parameter NM = 4, // Number of (outgoing) master ports
+ parameter LGFIFO = 4 // Size of outgoing FIFOs
+ ) (
+ input wire S_AXI_ACLK, S_AXI_ARESETN,
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ output wire [NM-1:0] M_AXIS_TVALID,
+ input wire [NM-1:0] M_AXIS_TREADY,
+ output wire [NM*C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA
+ );
+
+ // Local declarations
+ // {{{
+ localparam DW = C_AXIS_DATA_WIDTH;
+ genvar gk;
+ wire [NM-1:0] fifo_full;
+ wire skd_valid, axis_ready;
+ wire [DW-1:0] skd_data;
+ wire [NM*(LGFIFO+1)-1:0] ign_fifo_fill;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming skid buffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // This makes it so that we can control VALID && DATA (and so
+ // backpressure) with a combinatorial value, something that would
+ // otherwise be against protocol.
+ //
+ skidbuffer #(
+ .DW(DW),
+ .OPT_OUTREG(1'b0)
+ ) tskd (
+ .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIS_TVALID), .o_ready(S_AXIS_TREADY),
+ .i_data(S_AXIS_TDATA),
+ .o_valid(skd_valid), .i_ready(axis_ready),
+ .o_data(skd_data)
+ );
+
+ assign axis_ready = skd_valid && fifo_full == 0;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing FIFOs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate for(gk=0; gk<NM; gk=gk+1)
+ begin : OUTGOING_FIFOS
+ wire fifo_empty;
+ sfifo #(
+ .BW(DW),
+ .LGFLEN(LGFIFO)
+ ) fifo (
+ .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_wr(axis_ready), .i_data(skd_data),
+ .o_full(fifo_full[gk]),
+ .o_fill(ign_fifo_fill[gk*(LGFIFO+1)+:LGFIFO+1]),
+ .i_rd(M_AXIS_TVALID[gk] && M_AXIS_TREADY[gk]),
+ .o_data(M_AXIS_TDATA[gk*DW +: DW]),
+ .o_empty(fifo_empty)
+ );
+
+ assign M_AXIS_TVALID[gk] = !fifo_empty;
+
+ end endgenerate
+ // }}}
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, ign_fifo_fill };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = 31;
+ reg [F_LGDEPTH-1:0] icount;
+ (* anyconst *) reg [$clog2(NM)-1:0] fc_channel;
+ reg [F_LGDEPTH-1:0] ocount;
+ reg f_past_valid;
+ reg [LGFIFO:0] fifo_fill;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ icount <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ icount <= icount + 1;
+
+ assign fifo_fill = ign_fifo_fill[fc_channel * (LGFIFO+1) +: LGFIFO+1];
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ ocount <= 0;
+ else if (M_AXIS_TVALID[fc_channel] && M_AXIS_TREADY[fc_channel])
+ ocount <= ocount + 1;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !$past(S_AXI_ARESETN))
+ assume(!S_AXIS_TVALID);
+ else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ begin
+ assume(S_AXIS_TVALID);
+ assume($stable(S_AXIS_TDATA));
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN)
+ begin
+ if (!$past(S_AXI_ARESETN))
+ assert(!M_AXIS_TVALID[fc_channel]);
+ else if ($past(M_AXIS_TVALID[fc_channel] && !M_AXIS_TREADY[fc_channel]))
+ begin
+ assert(M_AXIS_TVALID[fc_channel]);
+ assert($stable(M_AXIS_TDATA[fc_channel * DW +: DW]));
+ end
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(icount == ocount + (S_AXIS_TREADY ? 0:1) + fifo_fill);
+ always @(*)
+ assume(!icount[F_LGDEPTH-1]);
+`endif // FORMAL
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axisgdma.v b/rtl/wb2axip/axisgdma.v
new file mode 100644
index 0000000..dac2cbc
--- /dev/null
+++ b/rtl/wb2axip/axisgdma.v
@@ -0,0 +1,1120 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisgdma.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Scripts an AXI DMA via in-memory tables: reads from the tables,
+// commands the DMA.
+//
+// Registers:
+//
+// 0. Control
+// 8b KEY
+// 3'b PROT
+// 4'b QOS
+// 1b Abort: Either aborting or aborted
+// 1b Err: Ended on an error
+// 1b Busy
+// 1b Interrupt Enable
+// 1b Interrupt Set
+// 1b Start
+// 1. Reserved
+// 2-3. First table entry address
+// (Current) table entry address on read, if in progress
+// (Optional)
+// 4-5. Current read address
+// 6-7. Current write address
+// 1. Remaining amount to be written (this entry)
+//
+// Table entries (must be 32-bit aligned):
+// If (address_width > 30)
+// 64b: { 2'bflags, 62'b SOURCE ADDRESS (bytes) }
+// 00: Continue after this to next
+// 01: Skip this address
+// 10: Jump to new address
+// 11: Last item in chain
+// 64b: { int_en, 1'b0, DESTINATION ADDRESS (bytes) }
+// 32b LENGTH (in bytes)
+// else
+// 32b: { 2'bflags, 30'b SOURCE ADDRESS (bytes) }
+// 32b: { int_en, 1'b0, 30'b DESTINATION ADDRESS (bytes) }
+// 32b LENGTH (in bytes)
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// `define AXI3
+// }}}
+module axisgdma #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 64,
+ //
+ localparam C_AXIL_ADDR_WIDTH = 4,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ //
+ // OPT_UNALIGNED turns on support for unaligned addresses,
+ // whether source, destination, or length parameters.
+ parameter [0:0] OPT_UNALIGNED = 1'b1,
+ //
+ // OPT_WRAPMEM controls what happens if the transfer runs off
+ // of the end of memory. If set, the transfer will continue
+ // again from the beginning of memory. If clear, the transfer
+ // will be aborted with an error if either read or write
+ // address ever get this far.
+ parameter [0:0] OPT_WRAPMEM = 1'b1,
+ //
+ // LGMAXBURST controls the size of the maximum burst produced
+ // by this core. Specifically, its the log (based 2) of that
+ // maximum size. Hence, for AXI4, this size must be 8
+ // (i.e. 2^8 or 256 beats) or less. For AXI3, the size must
+ // be 4 or less. Tests have verified performance for
+ // LGMAXBURST as low as 2. While I expect it to fail at
+ // LGMAXBURST=0, I haven't verified at what value this burst
+ // parameter is too small.
+`ifdef AXI3
+ parameter LGMAXBURST=4, // 16 beats max
+`else
+ parameter LGMAXBURST=8, // 256 beats
+`endif
+ //
+ // LGFIFO: This is the (log-based-2) size of the internal FIFO.
+ // Hence if LGFIFO=8, the internal FIFO will have 256 elements
+ // (words) in it. High throughput transfers are accomplished
+ // by first storing data into a FIFO, then once a full burst
+ // size is available bursting that data over the bus. In
+ // order to be able to keep receiving data while bursting it
+ // out, the FIFO size must be at least twice the size of the
+ // maximum burst size. Larger sizes are possible as well.
+ parameter LGFIFO = LGMAXBURST+1, // 512 element FIFO
+ //
+ // LGLEN: specifies the number of bits in the transfer length
+ // register. If a transfer cannot be specified in LGLEN bits,
+ // it won't happen. LGLEN must be less than or equal to the
+ // address width.
+ parameter LGLEN = C_AXI_ADDR_WIDTH,
+ //
+ // AXI uses ID's to transfer information. This core rather
+ // ignores them. Instead, it uses a constant ID for all
+ // transfers. The following two parameters control that ID.
+ parameter [C_AXI_ID_WIDTH-1:0] DMA_READ_ID = 0,
+ parameter [C_AXI_ID_WIDTH-1:0] DMA_WRITE_ID = 0,
+ parameter [C_AXI_ID_WIDTH-1:0] PF_READ_ID = DMA_READ_ID+1,
+ //
+ // The "ABORT_KEY" is a byte that, if written to the control
+ // word while the core is running, will cause the data transfer
+ // to be aborted.
+ parameter [7:0] ABORT_KEY = 8'h6d,
+ //
+ // OPT_LOWPOWER
+ parameter [0:0] OPT_LOWPOWER = 1'b0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI4-lite control interface
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output reg S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output reg S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output reg [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ //
+ //
+ // The AXI Master (DMA) interface
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_AWLEN,
+`else
+ output wire [7:0] M_AXI_AWLEN,
+`endif
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_AWLOCK,
+`else
+ output wire M_AXI_AWLOCK,
+`endif
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+`ifdef AXI3
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+`endif
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output reg M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ //
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_ARLEN,
+`else
+ output wire [7:0] M_AXI_ARLEN,
+`endif
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_ARLOCK,
+`else
+ output wire M_AXI_ARLOCK,
+`endif
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP,
+ //
+ output reg o_int
+ // }}}
+ );
+
+ // Local parameter definitions
+ // {{{
+ // The number of beats in this maximum burst size is
+ // automatically determined from LGMAXBURST, and so its
+ // forced to be a power of two this way.
+ // localparam MAXBURST=(1<<LGMAXBURST);
+ //
+ localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam AXILLSB= $clog2(C_AXIL_DATA_WIDTH)-3;
+ // localparam LGLENW= LGLEN-ADDRLSB;
+
+ localparam [1:0] CTRL_ADDR = 2'b00,
+ // UNUSED_ADDR = 2'b01,
+ TBLLO_ADDR = 2'b10,
+ TBLHI_ADDR = 2'b11;
+ localparam CTRL_START_BIT = 0,
+ CTRL_BUSY_BIT = 0,
+ CTRL_INT_BIT = 1,
+ CTRL_INTEN_BIT = 2,
+ CTRL_ABORT_BIT = 3,
+ CTRL_ERR_BIT = 4;
+ // CTRL_INTERIM_BIT= 5;
+ localparam [1:0] AXI_INCR = 2'b01, AXI_OKAY = 2'b00;
+
+`ifdef AXI3
+ localparam LENWIDTH = 4;
+`else
+ localparam LENWIDTH = 8;
+`endif
+
+ // DMA device internal addresses
+ // {{{
+ localparam [4:0] DMA_CONTROL= 5'b00000;
+ // }}}
+
+ // localparam [C_AXI_ADDR_WIDTH-1:0] TBL_SIZE
+ // = (C_AXI_ADDR_WIDTH < 30) ? (4*5) : (4*7);
+
+ // }}}
+
+ // Register/net declarations
+ // {{{
+ reg axil_write_ready, axil_read_ready;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_tbl, new_widetbl;
+ reg [C_AXI_ADDR_WIDTH-1:0] tbl_addr, r_tbl_addr;
+ reg r_int_enable, r_int, r_err, r_abort;
+ wire w_int, fsm_err;
+
+ reg [3:0] r_qos;
+ reg [2:0] r_prot;
+ reg r_start;
+ wire r_done, r_busy;
+
+ wire awskd_valid;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+
+ wire arskd_valid;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+
+ // Prefetch interface registers
+ // {{{
+ wire new_pc, pf_ready, pf_clear_cache;
+ wire [C_AXI_ADDR_WIDTH-1:0] ipc;
+ wire [31:0] pf_insn;
+ wire pf_valid, pf_illegal;
+ wire pf_axi_arvalid;
+ reg pf_axi_arready;
+ wire [C_AXI_ADDR_WIDTH-1:0] pf_axi_araddr, pf_pc;
+ wire pf_axi_rready_ignored;
+ wire [C_AXI_ID_WIDTH-1:0] pf_axi_arid;
+ wire [LENWIDTH-1:0] pf_axi_arlen;
+ wire [2:0] pf_axi_arsize;
+ wire [1:0] pf_axi_arburst;
+ wire [3:0] pf_axi_arcache;
+ wire [2:0] pf_axi_arprot;
+ wire [3:0] pf_axi_arqos;
+ // }}}
+
+ // DMA control registers/AXI-lite interface
+ // {{{
+ wire dmac_awready_ignored;
+ reg [4:0] dmac_waddr;
+ //
+ reg dmac_wvalid;
+ wire dmac_wready;
+ reg [31:0] dmac_wdata;
+ reg [3:0] dmac_wstrb;
+ //
+ wire dmac_bvalid;
+ wire [1:0] dmac_bresp;
+ //
+ wire dmac_arready;
+ wire dmac_rvalid;
+ wire [31:0] dmac_rdata;
+ wire [1:0] dmac_rresp;
+ // }}}
+
+ // DMA AXI nets
+ // {{{
+ wire sdma_arvalid;
+ wire [C_AXI_ID_WIDTH-1:0] sdma_arid;
+ wire [C_AXI_ADDR_WIDTH-1:0] sdma_araddr;
+ wire [LENWIDTH-1:0] sdma_arlen;
+ wire [2:0] sdma_arsize;
+ wire [1:0] sdma_arburst;
+ wire [0:0] sdma_arlock;
+ wire [3:0] sdma_arcache;
+ wire [2:0] sdma_arprot;
+ wire [3:0] sdma_arqos;
+ reg sdma_arready;
+ wire sdma_rready_ignored;
+ wire dma_complete;
+
+ wire unused_dma_lock;
+ // }}}
+
+ // Combined AXI nets
+ // {{{
+ reg m_axi_arvalid;
+ reg [C_AXI_ID_WIDTH-1:0] m_axi_arid;
+ reg [C_AXI_ADDR_WIDTH-1:0] m_axi_araddr;
+ reg [LENWIDTH-1:0] m_axi_arlen;
+ reg [2:0] m_axi_arsize;
+ reg [1:0] m_axi_arburst;
+ reg [3:0] m_axi_arcache;
+ reg [2:0] m_axi_arprot;
+ reg [3:0] m_axi_arqos;
+ // }}}
+
+ reg pf_wins_arbitration;
+ wire m_axi_arready;
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-Lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write control logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // axil AW skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+ // }}}
+
+ // axil W skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8)
+ // }}}
+ ) axilwskid (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WSTRB, S_AXIL_WDATA }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_strb, wskd_data })
+ // }}}
+ );
+ // }}}
+
+ // axil_write_ready
+ // {{{
+ always @(*)
+ begin
+ axil_write_ready = !S_AXIL_BVALID || S_AXIL_BREADY;
+ if (!awskd_valid || !wskd_valid)
+ axil_write_ready = 0;
+ end
+ // }}}
+
+ // S_AXIL_BVALID
+ // {{{
+ initial S_AXIL_BVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXIL_BVALID <= 1'b0;
+ else if (!S_AXIL_BVALID || S_AXIL_BREADY)
+ S_AXIL_BVALID <= axil_write_ready;
+ // }}}
+
+ // S_AXIL_BRESP
+ // {{{
+ assign S_AXIL_BRESP = AXI_OKAY;
+ // }}}
+
+ // r_start
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_start <= 1'b0;
+ else begin
+ r_start <= !r_busy && axil_write_ready && wskd_strb[0]
+ && wskd_data[CTRL_START_BIT]
+ && (awskd_addr == CTRL_ADDR);
+ if (r_err && !wskd_data[CTRL_ERR_BIT])
+ r_start <= 0;
+ if (r_abort && !wskd_data[CTRL_ABORT_BIT])
+ r_start <= 0;
+ end
+ // }}}
+
+ // r_err
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_err <= 1'b0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready)
+ r_err <= (r_err) && (!wskd_strb[0]
+ || !wskd_data[CTRL_ERR_BIT]);
+ end else begin
+ r_err <= r_err || fsm_err;
+ end
+ // }}}
+
+ // o_int
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !r_int_enable || !r_busy)
+ o_int <= 0;
+ else if (w_int)
+ o_int <= 1'b1;
+ // }}}
+
+ // r_int
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_int <= 0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready && awskd_addr == CTRL_ADDR
+ && wskd_strb[0])
+ begin
+ if (wskd_data[CTRL_START_BIT])
+ r_int <= 0;
+ else if (wskd_data[CTRL_INT_BIT])
+ r_int <= 0;
+ end
+ end else if (w_int)
+ r_int <= 1'b1;
+ // }}}
+
+ // r_abort
+ // {{{
+ initial r_abort = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_abort <= 1'b0;
+ else if (!r_busy)
+ begin
+ if (axil_write_ready && awskd_addr == CTRL_ADDR && wskd_strb[0])
+ begin
+ if(wskd_data[CTRL_START_BIT]
+ ||wskd_data[CTRL_ABORT_BIT]
+ ||wskd_data[CTRL_ERR_BIT])
+ r_abort <= 0;
+ end
+ end else if (!r_abort)
+ r_abort <= (axil_write_ready && awskd_addr == CTRL_ADDR)
+ &&(wskd_strb[3] && wskd_data[31:24] == ABORT_KEY);
+ // }}}
+
+ // wide_tbl, new_widetbl
+ // {{{
+ always @(*)
+ begin
+ wide_tbl = 0;
+ wide_tbl[C_AXI_ADDR_WIDTH-1:0] = r_tbl_addr;
+
+ new_widetbl = wide_tbl;
+ if (awskd_addr == TBLLO_ADDR)
+ new_widetbl[31:0] = apply_wstrb(wide_tbl[31:0],
+ wskd_data, wskd_strb);
+ if (awskd_addr == TBLHI_ADDR)
+ new_widetbl[63:32] = apply_wstrb(wide_tbl[63:32],
+ wskd_data, wskd_strb);
+ end
+ // }}}
+
+ // r_prot, r_qos, r_int_enable, r_tbl_addr
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_prot <= 0;
+ r_qos <= 0;
+ r_int_enable <= 0;
+ end else if (!r_busy && axil_write_ready)
+ begin
+ case(awskd_addr)
+ CTRL_ADDR: begin
+ if (wskd_strb[2])
+ begin
+ r_prot <= wskd_data[22:20];
+ r_qos <= wskd_data[19:16];
+ end
+ if (wskd_strb[0])
+ begin
+ r_int_enable <= wskd_data[CTRL_INTEN_BIT];
+ end end
+ TBLLO_ADDR: begin
+ r_tbl_addr <= new_widetbl[C_AXI_ADDR_WIDTH-1:0];
+ end
+ TBLHI_ADDR: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH) begin
+ r_tbl_addr <= new_widetbl[C_AXI_ADDR_WIDTH-1:0];
+ end
+ default: begin end
+ endcase
+ end else if (r_busy)
+ begin
+ r_tbl_addr <= tbl_addr;
+ end
+ // }}}
+
+ // apply_wstrb function
+ // {{{
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8] = wstrb[k] ? new_data[k*8 +: 8]
+ : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite control read interface
+ // {{{
+
+ // AXI-lite AR skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)
+ // }}}
+ ) axilarskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr)
+ // }}}
+ );
+ // }}}
+
+ // axil_read_ready
+ // {{{
+ always @(*)
+ begin
+ axil_read_ready = !S_AXIL_RVALID || S_AXIL_RREADY;
+ if (!arskd_valid)
+ axil_read_ready = 1'b0;
+ end
+ // }}}
+
+ // S_AXIL_RVALID
+ // {{{
+ initial S_AXIL_RVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXIL_RVALID <= 1'b0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ S_AXIL_RVALID <= axil_read_ready;
+ // }}}
+
+ // S_AXIL_RDATA
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ S_AXIL_RDATA <= 0;
+ else if (!S_AXIL_RVALID || S_AXIL_RREADY)
+ begin
+ S_AXIL_RDATA <= 0;
+ case(arskd_addr)
+ CTRL_ADDR: begin
+ S_AXIL_RDATA[CTRL_ERR_BIT] <= r_err;
+ S_AXIL_RDATA[CTRL_ABORT_BIT] <= r_abort;
+ S_AXIL_RDATA[CTRL_INTEN_BIT] <= r_int_enable;
+ S_AXIL_RDATA[CTRL_INT_BIT] <= r_int;
+ S_AXIL_RDATA[CTRL_BUSY_BIT] <= r_busy;
+ end
+ // Unused:
+ TBLLO_ADDR:
+ S_AXIL_RDATA <= wide_tbl[C_AXIL_DATA_WIDTH-1:0];
+ TBLHI_ADDR:
+ S_AXIL_RDATA <= wide_tbl[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ default: begin end
+ endcase
+
+ if (OPT_LOWPOWER && (!axil_read_ready || !arskd_valid))
+ S_AXIL_RDATA <= 0;
+ end
+ // }}}
+
+ // S_AXIL_RRESP
+ // {{{
+ assign S_AXIL_RRESP = AXI_OKAY;
+ // }}}
+ // }}} // AXI-lite read
+ // }}} // AXI-lite (all)
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // DMA wrapper
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Prefix: dmac for the sub DMA control interface
+ // Prefix: sdma for the sub DMA master interface
+ axidma #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_UNALIGNED(OPT_UNALIGNED),
+ .OPT_WRAPMEM(OPT_WRAPMEM),
+ .LGMAXBURST(LGMAXBURST),
+ .LGFIFO(LGFIFO),
+ .LGLEN(LGLEN),
+ .AXI_READ_ID(DMA_READ_ID),
+ .AXI_WRITE_ID(DMA_WRITE_ID),
+ .ABORT_KEY(ABORT_KEY)
+ // }}}
+ ) subdma(
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ //
+ // The AXI4-lite control interface
+ // {{{
+ .S_AXIL_AWVALID(dmac_wvalid), // Merge AW & W channels:DMA ok w/
+ .S_AXIL_AWREADY(dmac_awready_ignored),
+ .S_AXIL_AWADDR( dmac_waddr),
+ .S_AXIL_AWPROT( 3'h0), // Internally ignored
+ //
+ .S_AXIL_WVALID(dmac_wvalid),
+ .S_AXIL_WREADY(dmac_wready),
+ .S_AXIL_WDATA( dmac_wdata),
+ .S_AXIL_WSTRB( dmac_wstrb),
+ //
+ .S_AXIL_BVALID(dmac_bvalid),
+ .S_AXIL_BREADY(1'b1),
+ .S_AXIL_BRESP( dmac_bresp),
+ //
+ .S_AXIL_ARVALID(!S_AXI_ARESETN),
+ .S_AXIL_ARREADY(dmac_arready),
+ .S_AXIL_ARADDR( DMA_CONTROL),
+ .S_AXIL_ARPROT( 3'h0), // Internally ignored
+ //
+ .S_AXIL_RVALID(dmac_rvalid),
+ .S_AXIL_RREADY(1'b1),
+ .S_AXIL_RDATA( dmac_rdata),
+ .S_AXIL_RRESP( dmac_rresp),
+ // }}}
+ //
+ // The AXI Master (DMA) interface
+ // {{{
+ .M_AXI_AWVALID(M_AXI_AWVALID),
+ .M_AXI_AWREADY(M_AXI_AWREADY),
+ .M_AXI_AWID( M_AXI_AWID),
+ .M_AXI_AWADDR( M_AXI_AWADDR),
+ .M_AXI_AWLEN( M_AXI_AWLEN),
+ .M_AXI_AWSIZE( M_AXI_AWSIZE),
+ .M_AXI_AWBURST(M_AXI_AWBURST),
+ .M_AXI_AWLOCK( unused_dma_lock),
+ .M_AXI_AWCACHE(M_AXI_AWCACHE),
+ .M_AXI_AWPROT( M_AXI_AWPROT),
+ .M_AXI_AWQOS( M_AXI_AWQOS),
+ //
+ //
+ .M_AXI_WVALID(M_AXI_WVALID),
+ .M_AXI_WREADY(M_AXI_WREADY),
+`ifdef AXI3
+ .M_AXI_WID(M_AXI_WID),
+`endif
+ .M_AXI_WDATA(M_AXI_WDATA),
+ .M_AXI_WSTRB(M_AXI_WSTRB),
+ .M_AXI_WLAST(M_AXI_WLAST),
+ //
+ //
+ .M_AXI_BVALID(M_AXI_BVALID),
+ .M_AXI_BREADY(M_AXI_BREADY),
+ .M_AXI_BID( M_AXI_BID),
+ .M_AXI_BRESP( M_AXI_BRESP),
+ // }}}
+ // AXI master read interface
+ // {{{
+ // The read channel needs to be arbitrated
+ .M_AXI_ARVALID(sdma_arvalid),
+ .M_AXI_ARREADY(sdma_arready),
+ .M_AXI_ARID(sdma_arid),
+ .M_AXI_ARADDR(sdma_araddr),
+ .M_AXI_ARLEN(sdma_arlen),
+ .M_AXI_ARSIZE(sdma_arsize),
+ .M_AXI_ARBURST(sdma_arburst),
+ .M_AXI_ARLOCK(sdma_arlock),
+ .M_AXI_ARCACHE(sdma_arcache),
+ .M_AXI_ARPROT(sdma_arprot),
+ .M_AXI_ARQOS(sdma_arqos),
+ //
+ .M_AXI_RVALID(M_AXI_RVALID && M_AXI_RID == DMA_READ_ID),
+ .M_AXI_RREADY(sdma_rready_ignored), // Known to be one
+ .M_AXI_RID( DMA_READ_ID),
+ .M_AXI_RDATA(M_AXI_RDATA),
+ .M_AXI_RLAST(M_AXI_RLAST),
+ .M_AXI_RRESP(M_AXI_RRESP),
+ // }}}
+ .o_int(dma_complete)
+ // }}}
+ );
+
+ assign M_AXI_AWLOCK = 0;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-Lite prefetch
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // The AXI_lite fetch submodule
+ // {{{
+ axilfetch #(
+ // {{{
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .FETCH_LIMIT(4)
+ // }}}
+ ) pf (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ //
+ // "CPU" interface
+ // {{{
+ .i_cpu_reset(!S_AXI_ARESETN),
+ .i_new_pc(new_pc),
+ .i_clear_cache(pf_clear_cache),
+ .i_ready(pf_ready),
+ .i_pc(ipc),
+ .o_insn(pf_insn),
+ .o_valid(pf_valid),
+ .o_pc(pf_pc),
+ .o_illegal(pf_illegal),
+ // }}}
+ // AXI-lite interface
+ // {{{
+ .M_AXI_ARVALID(pf_axi_arvalid),
+ .M_AXI_ARREADY(pf_axi_arready),
+ .M_AXI_ARADDR( pf_axi_araddr),
+ .M_AXI_ARPROT( pf_axi_arprot),
+ //
+ .M_AXI_RVALID( M_AXI_RVALID && M_AXI_RID == PF_READ_ID),
+ .M_AXI_RREADY( pf_axi_rready_ignored), // Always 1'b1
+ .M_AXI_RDATA( M_AXI_RDATA),
+ .M_AXI_RRESP( M_AXI_RRESP)
+ // }}}
+ // }}}
+ );
+ // }}}
+
+ assign pf_axi_arid = PF_READ_ID;
+ assign pf_axi_arlen = 0; // Only read singletons
+ assign pf_axi_arsize = ADDRLSB[2:0];
+ assign pf_axi_arburst = AXI_INCR;
+ assign pf_axi_arcache = 4'b0011;
+ // assign pf_axi_arprot = 3'b100;
+ assign pf_axi_arqos = 4'h0;
+
+ assign M_AXI_RREADY = 1'b1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // PF vs DMA arbiter
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // pf_wins_arbitration
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!m_axi_arvalid || m_axi_arready)
+ begin
+ if (pf_axi_arvalid && !sdma_arvalid)
+ pf_wins_arbitration <= 1'b1;
+ else
+ pf_wins_arbitration <= 1'b0;
+ end
+ // }}}
+
+ // m_axi_*
+ // {{{
+ always @(*)
+ begin
+ if (pf_wins_arbitration)
+ begin
+ m_axi_arvalid = pf_axi_arvalid;
+ m_axi_arid = pf_axi_arid;
+ m_axi_araddr = pf_axi_araddr;
+ m_axi_arlen = pf_axi_arlen;
+ m_axi_arsize = pf_axi_arsize;
+ m_axi_arburst = pf_axi_arburst;
+ m_axi_arcache = pf_axi_arcache;
+ m_axi_arprot = pf_axi_arprot;
+ m_axi_arqos = pf_axi_arqos;
+ end else begin
+ m_axi_arvalid = sdma_arvalid;
+ m_axi_arid = sdma_arid;
+ m_axi_araddr = sdma_araddr;
+ m_axi_arlen = sdma_arlen;
+ m_axi_arsize = sdma_arsize;
+ m_axi_arburst = sdma_arburst;
+ m_axi_arcache = sdma_arcache;
+ m_axi_arprot = sdma_arprot;
+ m_axi_arqos = sdma_arqos;
+ end
+ end
+ // }}}
+
+ // *_arready
+ // {{{
+ always @(*)
+ begin
+ sdma_arready = m_axi_arready && !pf_wins_arbitration;
+ pf_axi_arready = m_axi_arready && pf_wins_arbitration;
+ end
+ // }}}
+
+ // Outgoing AR skid buffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(1'b1),
+ .DW(C_AXI_ID_WIDTH + C_AXI_ADDR_WIDTH + LENWIDTH
+ + 3 + 2 + 4 +3 + 4)
+ // }}}
+ ) marskd(
+ // {{{
+ S_AXI_ACLK, !S_AXI_ARESETN, m_axi_arvalid, m_axi_arready,
+ { m_axi_arid, m_axi_araddr, m_axi_arlen, m_axi_arsize,
+ m_axi_arburst, m_axi_arcache,
+ m_axi_arprot, m_axi_arqos },
+ M_AXI_ARVALID, M_AXI_ARREADY,
+ { M_AXI_ARID, M_AXI_ARADDR, M_AXI_ARLEN, M_AXI_ARSIZE,
+ M_AXI_ARBURST, M_AXI_ARCACHE,
+ M_AXI_ARPROT, M_AXI_ARQOS }
+ // }}}
+ );
+
+`ifdef AXI3
+ assign M_AXI_ARLOCK = 2'b00; // We don't use lock anyway
+`else
+ assign M_AXI_ARLOCK = 1'b0;
+`endif
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FSM Control states
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ axisgfsm #(
+ // {{{
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .ABORT_KEY(ABORT_KEY)
+ // }}}
+ ) fsm (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ // Control interface
+ // {{{
+ .i_start(r_start),
+ .i_abort(r_abort),
+ .i_tbl_addr(r_tbl_addr),
+ .i_qos(r_qos),
+ .i_prot(r_prot),
+ .o_done(r_done),
+ .o_busy(r_busy),
+ .o_int(w_int),
+ .o_err(fsm_err),
+ .o_tbl_addr(tbl_addr),
+ // }}}
+ // Prefetch interface
+ // {{{
+ .o_new_pc(new_pc),
+ .o_pf_clear_cache(pf_clear_cache),
+ .o_pf_ready(pf_ready),
+ .o_pf_pc(ipc),
+ .i_pf_valid(pf_valid),
+ .i_pf_insn(pf_insn),
+ .i_pf_pc(pf_pc),
+ .i_pf_illegal(pf_illegal),
+ // }}}
+ // DMA AXI-lite control interface
+ // {{{
+ .o_dmac_wvalid(dmac_wvalid),
+ .i_dmac_wready(dmac_wready),
+ .o_dmac_waddr(dmac_waddr),
+ .o_dmac_wdata(dmac_wdata),
+ .o_dmac_wstrb(dmac_wstrb),
+ .i_dmac_rdata(dmac_rdata),
+ .i_dma_complete(dma_complete)
+ // }}}
+
+ // }}}
+ );
+
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, dmac_awready_ignored, dmac_bvalid,
+ dmac_bresp, dmac_rvalid, dmac_rresp,
+ S_AXIL_AWADDR[AXILLSB-1:0], S_AXIL_AWPROT,
+ S_AXIL_ARADDR[AXILLSB-1:0], S_AXIL_ARPROT,
+ M_AXI_RREADY, r_done,
+ new_widetbl[63:C_AXI_ADDR_WIDTH],
+ unused_dma_lock,
+ sdma_arlock, sdma_rready_ignored,
+ pf_axi_rready_ignored, dmac_arready };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties (neither written, nor tested)
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The full formal verification of this core has not been completed.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxil_slave #(
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH)
+ //
+ // ...
+ //
+ )
+ faxil(
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr(S_AXIL_AWADDR),
+ .i_axi_awprot(S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP)
+ //
+ // ...
+ //
+ );
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The main AXI data interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions (i.e. constraints)
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // None (currently)
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axisgfsm.v b/rtl/wb2axip/axisgfsm.v
new file mode 100644
index 0000000..ea01515
--- /dev/null
+++ b/rtl/wb2axip/axisgfsm.v
@@ -0,0 +1,1221 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisgfsm.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Scripts an AXI DMA via in-memory tables: reads from the tables,
+// commands the DMA.
+//
+// Registers:
+//
+// 0. Control
+// 8b KEY
+// 3'b PROT
+// 4'b QOS
+// 1b Abort: Either aborting or aborted
+// 1b Err: Ended on an error
+// 1b Busy
+// 1b Interrupt Enable
+// 1b Interrupt Set
+// 1b Start
+// 1. Reserved
+// 2-3. First table entry address
+// (Current) table entry address on read, if in progress
+// (Optional)
+// 4-5. Current read address
+// 6-7. Current write address
+// 1. Remaining amount to be written (this entry)
+//
+// Table format:
+// Table entries either consist of five 32-bit words, or three 32-bit
+// words, depending upon the size of the address bus.
+//
+// Address bus > 30 bits: five 32-bit words
+// 0-1: 64b: { 2'bflags, 62'b SOURCE ADDRESS (bytes) }
+// 00: Continue after this to next
+// 01: Last item in chain
+// 10: Skip this address
+// 11: Jump to new address
+// 2-3: 64b: { int_en, 1'b0, DESTINATION ADDRESS (bytes) }
+// 4: 32b Transfer length (in bytes)
+//
+// Address bus <= 30 bits: three 32-bit words
+// 0: 32b: { 2'bflags, 30'b SOURCE ADDRESS (bytes) }
+// 1: 32b: { int_en, 1'b0, 30'b DESTINATION ADDRESS (bytes) }
+// 2: 32b LENGTH (in bytes)
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// `define AXI3
+// }}}
+module axisgfsm #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 30,
+ // Verilator lint_off UNUSED
+ parameter C_AXI_DATA_WIDTH = 32,
+ // Verilator lint_on UNUSED
+ localparam DMAC_ADDR_WIDTH = 5,
+ localparam DMAC_DATA_WIDTH = 32,
+ //
+ // The "ABORT_KEY" is a byte that, if written to the control
+ // word while the core is running, will cause the data transfer
+ // to be aborted.
+ parameter [7:0] ABORT_KEY = 8'h6d
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The control interface
+ // {{{
+ input wire i_start, i_abort,
+ input wire [C_AXI_ADDR_WIDTH-1:0] i_tbl_addr,
+ input wire [3:0] i_qos,
+ input wire [2:0] i_prot,
+ output reg o_busy,
+ output reg o_done,
+ output reg o_int,
+ output reg o_err,
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_tbl_addr,
+ // }}}
+ //
+ // The prefetch interface
+ // {{{
+ output reg o_new_pc,
+ output reg o_pf_clear_cache,
+ output reg o_pf_ready,
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_pf_pc,
+ input wire i_pf_valid,
+ input wire [31:0] i_pf_insn,
+ input wire [C_AXI_ADDR_WIDTH-1:0] i_pf_pc,
+ input wire i_pf_illegal,
+ // }}}
+ //
+ // The DMA submodule's AXI4-lite control interface
+ // {{{
+ output reg o_dmac_wvalid,
+ input wire i_dmac_wready,
+ output reg [DMAC_ADDR_WIDTH-1:0] o_dmac_waddr,
+ output reg [DMAC_DATA_WIDTH-1:0] o_dmac_wdata,
+ output reg [DMAC_DATA_WIDTH/8-1:0] o_dmac_wstrb,
+ input wire [DMAC_DATA_WIDTH-1:0] i_dmac_rdata,
+ input wire i_dma_complete
+ // }}}
+ // }}}
+ );
+
+ // Local parameter definitions
+ // {{{
+
+ // Scatter gather state machine states
+ // {{{
+ // States:
+ // - 0. IDLE (nothing going on, waiting for a new program counter)
+ // Enter on completion, reset, or (abort and DMA complete)
+ // - 1. READ_LOSRC -> DMA
+ // - 2. READ_HISRC -> DMA (Optional)
+ // ((Skip || JUMP) ->READ_LOSRC)
+ // - 3. READ_LODST -> DMA
+ // - 4. READ_HIDST -> DMA (Optional)
+ // - 5. READ_LEN -> DMA
+ // - 6. READ_FLAGS -> SETS -> START DMA
+ // - 7. Wait on DMA
+ // (Set interrupt if INT complete)
+ // (If last, go to idle, else jump to #1)
+ // (Set LAST on abort)
+ //
+ localparam [2:0] SG_IDLE = 3'h0,
+ SG_SRCHALF = 3'h1,
+ SG_SRCADDR = 3'h2,
+ SG_DSTHALF = 3'h3,
+ SG_DSTADDR = 3'h4,
+ SG_LENGTH = 3'h5,
+ SG_CONTROL = 3'h6,
+ SG_WAIT = 3'h7;
+ // }}}
+
+ // DMA device internal addresses
+ // {{{
+ // Verilator lint_off UNUSED
+ localparam [4:0] DMA_CONTROL= 5'b00000,
+ DMA_UNUSED = 5'b00100,
+ DMA_SRCLO = 5'b01000,
+ DMA_SRCHI = 5'b01100,
+ DMA_DSTLO = 5'b10000,
+ DMA_DSTHI = 5'b10100,
+ DMA_LENLO = 5'b11000,
+ DMA_LENHI = 5'b11100;
+ // Verilator lint_on UNUSED
+ // }}}
+
+ localparam [C_AXI_ADDR_WIDTH-1:0] TBL_SIZE
+ = (C_AXI_ADDR_WIDTH <= 30) ? (4*3) : (4*5);
+
+ // }}}
+
+ // Register/net declarations
+ // {{{
+ reg tbl_last, tbl_int_enable;
+ reg [2:0] sgstate;
+ reg dma_err, dma_abort, dma_done, dma_busy, dma_starting,
+ dma_aborting;
+ reg [59:0] r_pf_pc;
+ reg dma_op_complete, dma_terminate;
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FSM Control states
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_pf_ready
+ // {{{
+ always @(*)
+ begin
+ case(sgstate)
+ SG_IDLE: o_pf_ready = 1'b0;
+ SG_SRCHALF: o_pf_ready = (!o_dmac_wvalid || i_dmac_wready);
+ SG_SRCADDR: o_pf_ready = (!o_dmac_wvalid || i_dmac_wready);
+ SG_DSTHALF: o_pf_ready = (!o_dmac_wvalid || i_dmac_wready);
+ SG_DSTADDR: o_pf_ready = (!o_dmac_wvalid || i_dmac_wready);
+ SG_LENGTH: o_pf_ready = (!o_dmac_wvalid || i_dmac_wready);
+ SG_CONTROL: o_pf_ready = 1'b0;
+ SG_WAIT: o_pf_ready = 1'b0;
+ endcase
+
+ if (!o_busy || o_pf_clear_cache || o_new_pc)
+ o_pf_ready = 0;
+ end
+ // }}}
+
+ // w_int
+ // {{{
+ always @(*)
+ begin
+ o_int = 1'b0;
+
+ if (sgstate == SG_WAIT && i_dma_complete
+ && (tbl_last || tbl_int_enable))
+ o_int = 1'b1;
+ if (i_pf_valid && o_pf_ready && i_pf_illegal)
+ o_int = 1'b1;
+ end
+ // }}}
+
+ // Returns from the DMA controller (one clock later)
+ // {{{
+ always @(*)
+ begin
+ dma_err = i_dmac_rdata[4];
+ dma_abort = i_dmac_rdata[3];
+ dma_done = i_dmac_rdata[1];
+ dma_busy = i_dmac_rdata[0];
+ end
+ // }}}
+
+ // dma_op_complete
+ // {{{
+ always @(*)
+ if (dma_busy || dma_starting || (o_dmac_wvalid && !i_dmac_wready))
+ dma_op_complete = 0;
+ else
+ dma_op_complete = (!o_dmac_wvalid || !o_dmac_wstrb[0]);
+ // }}}
+
+ // dma_terminate
+ // {{{
+ always @(*)
+ begin
+ dma_terminate = 0;
+ if (i_abort || tbl_last || dma_aborting)
+ dma_terminate = 1;
+ if (dma_err || i_pf_illegal)
+ dma_terminate = 1;
+ end
+ // }}}
+
+ // The GIANT FSM itself
+ // new_pc, pf_clear_cache, dmac_wvalid, dmac_waddr, dmac_wdata,
+ // dmac_wstrb, ipc,
+ // {{{
+ initial r_pf_pc = 0;
+ initial sgstate = SG_IDLE;
+ initial o_new_pc = 1'b0;
+ initial o_busy = 1'b0;
+ initial o_done = 1'b0;
+ initial o_err = 1'b0;
+ initial o_dmac_wvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ // Auto-cleared values
+ // {{{
+ o_new_pc <= 1'b0;
+ o_pf_clear_cache <= 1'b0;
+ if (i_dmac_wready)
+ o_dmac_wvalid <= 1'b0;
+ if (!o_dmac_wvalid || i_dmac_wready)
+ o_dmac_wstrb <= 4'hf;
+ o_done <= 1'b0;
+ o_err <= 1'b0;
+ // }}}
+
+ case(sgstate)
+ SG_IDLE: // IDLE -- waiting for start
+ // {{{
+ begin
+ r_pf_pc[C_AXI_ADDR_WIDTH-1:0] <= i_tbl_addr;
+ if (i_start)
+ begin
+ o_new_pc <= 1'b1;
+ if (C_AXI_ADDR_WIDTH > 30)
+ sgstate <= SG_SRCHALF;
+ else
+ sgstate <= SG_SRCADDR;
+ o_busy <= 1'b1;
+ end else begin
+ o_new_pc <= 1'b0;
+ o_pf_clear_cache <= 1'b1;
+ o_busy <= 1'b0;
+ end end
+ // }}}
+ SG_SRCHALF: // Get the first source address
+ // {{{
+ if (i_pf_valid && (!o_dmac_wvalid || i_dmac_wready))
+ begin
+ o_dmac_wvalid <= 1'b1;
+ o_dmac_waddr <= DMA_SRCLO;
+ o_dmac_wdata <= i_pf_insn;
+ sgstate <= SG_SRCADDR;
+ // r_pf_pc[31:0] <= i_pf_insn;
+ o_tbl_addr <= i_pf_pc;
+ end
+ // }}}
+ SG_SRCADDR: // Second half of the source address
+ // {{{
+ if (i_pf_valid && o_pf_ready)
+ begin
+ o_dmac_wvalid <= !i_pf_insn[31];
+ o_dmac_waddr <= (C_AXI_ADDR_WIDTH<=30)
+ ? DMA_SRCLO : DMA_SRCHI;
+ o_dmac_wdata <= i_pf_insn;
+ // r_pf_pc[31:0] <= i_pf_insn;
+ if (C_AXI_ADDR_WIDTH <= 30)
+ begin
+ sgstate <= SG_DSTADDR;
+ o_tbl_addr <= i_pf_pc;
+ // // r_pf_pc[30-1:0] <= i_pf_insn[30-1:0];
+ end else begin
+ sgstate <= SG_DSTHALF;
+ // r_pf_pc[60-1:30] <= i_pf_insn[30-1:0];
+ end
+
+ tbl_last <= 1'b0;
+ case(i_pf_insn[31:30])
+ 2'b00: // Other items still to follow
+ tbl_last <= 1'b0;
+ 2'b01: // Last item in the chain
+ tbl_last <= 1'b1;
+ 2'b10: begin // Skip
+ tbl_last <= 1'b0;
+ if (C_AXI_ADDR_WIDTH > 30)
+ sgstate <= SG_SRCHALF;
+ else
+ sgstate <= SG_SRCADDR;
+ o_new_pc <= 1'b1;
+ r_pf_pc[C_AXI_ADDR_WIDTH-1:0]
+ <= r_pf_pc[C_AXI_ADDR_WIDTH-1:0] + TBL_SIZE;
+ end
+ 2'b11: begin // Jump
+ o_new_pc <= 1'b1;
+ // ipc <= // already set from above
+ if (C_AXI_ADDR_WIDTH > 30)
+ sgstate <= SG_SRCHALF;
+ else
+ sgstate <= SG_SRCADDR;
+ r_pf_pc[C_AXI_ADDR_WIDTH-1:0] <= i_pf_insn[C_AXI_ADDR_WIDTH-1:0];
+ end
+ endcase
+ end
+ // }}}
+ SG_DSTHALF: // Get the first half of the destination address
+ // {{{
+ if (i_pf_valid && (!o_dmac_wvalid || i_dmac_wready))
+ begin
+ o_dmac_wvalid<= 1'b1;
+ o_dmac_waddr <= DMA_DSTLO;
+ o_dmac_wdata <= i_pf_insn;
+ sgstate <= SG_DSTADDR;
+ end
+ // }}}
+ SG_DSTADDR: // Second half of the destination address
+ // {{{
+ if (i_pf_valid && (!o_dmac_wvalid || i_dmac_wready))
+ begin
+ o_dmac_wvalid <= 1'b1;
+ o_dmac_waddr <= (C_AXI_ADDR_WIDTH<=30)
+ ? DMA_DSTLO : DMA_DSTHI;
+ o_dmac_wdata <= { 2'b0, i_pf_insn[29:0] };
+ sgstate <= SG_LENGTH;
+ tbl_int_enable <= i_pf_insn[31];
+ end
+ // }}}
+ SG_LENGTH: // The length word
+ // {{{
+ if (i_pf_valid && (!o_dmac_wvalid || i_dmac_wready))
+ begin
+ o_dmac_wvalid <= 1'b1;
+ o_dmac_waddr <= DMA_LENLO;
+ o_dmac_wdata <= i_pf_insn;
+ sgstate <= SG_CONTROL;
+ if (i_pf_insn <= 0)
+ begin
+ o_dmac_wvalid <= 1'b0;
+ if (tbl_last)
+ begin
+ sgstate <= SG_IDLE;
+ o_new_pc <= 1'b0;
+ o_busy <= 1'b0;
+ o_done <= 1'b1;
+ o_pf_clear_cache <= 1'b1;
+ end else begin
+ sgstate <= SG_SRCADDR;
+ o_new_pc <= 1'b1;
+ end
+ end
+ end
+ // }}}
+ SG_CONTROL: // The control word to get us started
+ // {{{
+ if (!o_dmac_wvalid || i_dmac_wready)
+ begin
+ o_dmac_wvalid <= 1'b1;
+ o_dmac_waddr <= DMA_CONTROL;
+ o_dmac_wdata <= { 9'h0, i_prot, i_qos,
+ 11'h0,
+ 1'h1, // Clear any errors
+ 1'b1, // Clr abort flag
+ 1'b1, // Set int enable, int
+ 1'b1, // Clr any prior interrupt
+ 1'b1 };
+ sgstate <= SG_WAIT;
+ o_tbl_addr <= o_tbl_addr + TBL_SIZE;
+ end
+ // }}}
+ SG_WAIT: // Wait for the DMA transfer to complete
+ // {{{
+ if (dma_op_complete)
+ begin
+ // o_int <= int_enable;
+ r_pf_pc[C_AXI_ADDR_WIDTH-1:0] <= o_tbl_addr;
+ if (dma_terminate)
+ begin
+ o_pf_clear_cache <= 1'b1;
+ sgstate <= SG_IDLE;
+ o_busy <= 1'b0;
+ o_done <= 1'b1;
+ o_err <= dma_err || dma_abort;
+ if (i_pf_illegal)
+ r_pf_pc[C_AXI_ADDR_WIDTH-1:0] <= i_pf_pc;
+ end else if (C_AXI_ADDR_WIDTH > 30)
+ sgstate <= SG_SRCHALF;
+ else
+ sgstate <= SG_SRCADDR;
+ end
+ // }}}
+ endcase
+
+ if (i_pf_valid && i_pf_illegal
+ && sgstate != SG_CONTROL && sgstate != SG_WAIT
+ && !o_new_pc && !o_pf_clear_cache)
+ begin
+ // {{{
+ sgstate <= SG_IDLE;
+ o_pf_clear_cache <= 1'b1;
+ o_done <= 1'b1;
+ o_busy <= 1'b0;
+ o_err <= 1'b1;
+ // }}}
+ end
+
+ if (i_abort && (!o_dmac_wvalid || i_dmac_wready))
+ begin
+ // {{{
+ o_dmac_wstrb <= 4'h8;
+ o_dmac_wdata[31:24] <= ABORT_KEY;
+ o_dmac_wvalid <= o_dmac_wstrb[0] && dma_busy
+ && !dma_err && (sgstate == SG_WAIT);
+ // }}}
+ if (!dma_busy && (sgstate != SG_WAIT))
+ begin
+ sgstate <= SG_IDLE;
+ o_done <= 1'b1;
+ o_new_pc<= 1'b0;
+ o_busy <= 1'b0;
+ o_dmac_wvalid <= 1'b0;
+ o_pf_clear_cache <= 1'b1;
+ end
+ end
+
+ if (!S_AXI_ARESETN)
+ begin
+ // {{{
+ sgstate <= SG_IDLE;
+ o_pf_clear_cache <= 1'b1;
+ o_new_pc <= 1'b0;
+ o_dmac_wvalid <= 1'b0;
+ r_pf_pc <= 0;
+ o_busy <= 1'b0;
+ o_done <= 1'b0;
+ o_err <= 1'b0;
+ // }}}
+ end
+
+ r_pf_pc[60-1:C_AXI_ADDR_WIDTH] <= 0;
+ end
+ // }}}
+
+ // dma_starting
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ dma_starting <= 0;
+ else if (sgstate != SG_WAIT)
+ dma_starting <= 0;
+ else if (!o_dmac_wvalid || o_dmac_waddr != DMA_CONTROL)
+ dma_starting <= 0;
+ else
+ dma_starting <= o_dmac_wdata[0] && o_dmac_wstrb[0];
+
+ always @(*)
+ o_pf_pc = { r_pf_pc[C_AXI_ADDR_WIDTH-1:2], 2'b00 };
+ // }}}
+
+ // dma_aborting
+ // {{{
+ initial dma_aborting = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ dma_aborting <= 0;
+ else if (sgstate != SG_WAIT)
+ dma_aborting <= 0;
+ else if (!dma_aborting)
+ begin
+ if (i_abort)
+ dma_aborting <= 1;
+ end else if (!dma_busy && !dma_starting && (!o_dmac_wvalid
+ || (i_dmac_wready && !o_dmac_wstrb[0])))
+ dma_aborting <= 0;
+`ifdef FORMAL
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (o_dmac_wvalid && !o_dmac_wstrb[0])
+ assert(dma_aborting);
+ if (sgstate != SG_WAIT && sgstate != SG_IDLE)
+ assert(!dma_aborting);
+ end
+`endif
+ // }}}
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilatar lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, dma_done, i_dmac_rdata[31:5],
+ i_dmac_rdata[2],
+ r_pf_pc[59:C_AXI_ADDR_WIDTH] };
+ // Verilatar lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ localparam F_LGDEPTH = 4;
+ // Verilator lint_off UNDRIVEN
+ (* anyseq *) reg [TBL_SIZE*8-1:0] f_tblentry;
+ (* anyconst *) reg f_never_abort;
+ (* anyseq *) reg f_dma_complete;
+ // Verilator lint_on UNDRIVEN
+ reg f_past_valid;
+ reg [C_AXI_ADDR_WIDTH-1 :0] f_tbladdr, f_pc;
+ reg f_tbl_last, f_tbl_skip, f_tbl_jump,
+ f_tbl_int_enable;
+ // reg [C_AXI_ADDR_WIDTH-1:0] f_tbl_src;
+ reg f_dma_arvalid, f_dma_arready;
+ reg f_dma_busy;
+
+
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ always @(*)
+ if (i_start)
+ begin
+ assume(!i_abort);
+ assume(i_tbl_addr[1:0] == 2'b00);
+ end
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Prefetch interface property checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Prefetch properties
+
+ assume property (@(posedge S_AXI_ACLK)
+ S_AXI_ARESETN && i_pf_valid && !o_pf_ready
+ && !o_pf_clear_cache && !o_new_pc
+ |=> i_pf_valid && $stable({
+ i_pf_insn, i_pf_pc, i_pf_illegal }));
+
+ always @(*)
+ if (o_new_pc)
+ assert(o_pf_pc[1:0] == 2'b00);
+
+ always @(posedge S_AXI_ACLK)
+ if (o_new_pc)
+ f_pc <= o_pf_pc;
+ else if (i_pf_valid && o_pf_ready)
+ f_pc <= f_pc + 4;
+
+ always @(*)
+ if (i_pf_valid)
+ assume(i_pf_pc == f_pc);
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_new_pc && !o_pf_clear_cache)
+ assert(f_pc[1:0] == 2'b00);
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN || o_new_pc
+ || o_pf_clear_cache))
+ begin
+ assume(!i_pf_illegal);
+ end else if (!$rose(i_pf_valid))
+ begin
+ assume(!$rose(i_pf_illegal));
+ end else
+ assume($stable(i_pf_illegal));
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite interface property checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [F_LGDEPTH-1:0] fdma_rd_outstanding, fdma_wr_outstanding,
+ fdma_awr_outstanding;
+ reg f_dma_bvalid, f_dma_rvalid;
+
+ faxil_master #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(DMAC_ADDR_WIDTH),
+ .F_OPT_BRESP(1'b1),
+ .F_OPT_RRESP(1'b0),
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(F_LGDEPTH)
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(o_dmac_wvalid),
+ .i_axi_awready(i_dmac_wready),
+ .i_axi_awaddr(o_dmac_waddr),
+ .i_axi_awprot( 3'h0),
+ //
+ .i_axi_wvalid(o_dmac_wvalid),
+ .i_axi_wready(i_dmac_wready),
+ .i_axi_wdata( o_dmac_wdata),
+ .i_axi_wstrb( o_dmac_wstrb),
+ //
+ .i_axi_bvalid(f_dma_bvalid),
+ .i_axi_bready(1'b1),
+ .i_axi_bresp( 2'b00),
+ //
+ .i_axi_arvalid(f_dma_arvalid),
+ .i_axi_arready(f_dma_arready),
+ .i_axi_araddr(DMA_CONTROL),
+ .i_axi_arprot(3'h0),
+ //
+ .i_axi_rvalid(f_dma_rvalid),
+ .i_axi_rready(1'b1),
+ .i_axi_rdata(i_dmac_rdata),
+ .i_axi_rresp(2'b00),
+ //
+ .f_axi_rd_outstanding(fdma_rd_outstanding),
+ .f_axi_wr_outstanding(fdma_wr_outstanding),
+ .f_axi_awr_outstanding(fdma_awr_outstanding)
+ // }}}
+ );
+
+ initial f_dma_arvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dma_arvalid <= 1'b0;
+ else
+ f_dma_arvalid <= 1'b1;
+
+ always @(*)
+ f_dma_arready = 1'b1;
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assert(fdma_awr_outstanding == fdma_wr_outstanding);
+ assert(fdma_wr_outstanding == (f_dma_bvalid ? 1:0));
+ assert(fdma_rd_outstanding <= 1);
+ end
+
+ assert property (@(posedge S_AXI_ACLK)
+ !S_AXI_ARESETN
+ |=> sgstate == SG_IDLE && !o_dmac_wvalid);
+
+
+ assert property (@(posedge S_AXI_ACLK)
+ S_AXI_ARESETN && o_dmac_wvalid && !i_dmac_wready
+ |=> o_dmac_wvalid && $stable({
+ o_dmac_waddr, o_dmac_wdata, o_dmac_wstrb }));
+
+ assert property (@(posedge S_AXI_ACLK)
+ fdma_wr_outstanding == fdma_awr_outstanding);
+
+ assert property (@(posedge S_AXI_ACLK)
+ fdma_wr_outstanding == (f_dma_bvalid ? 1:0));
+
+ initial f_dma_bvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dma_bvalid <= 0;
+ else if (o_dmac_wvalid && i_dmac_wready)
+ f_dma_bvalid <= 1;
+ else
+ f_dma_bvalid <= 0;
+
+ initial f_dma_rvalid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dma_rvalid <= 0;
+ else if (f_dma_arvalid)
+ f_dma_rvalid <= 1;
+ else
+ f_dma_rvalid <= 0;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // DMA busy and read interface properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ initial f_dma_busy = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dma_busy <= 0;
+ else if (o_dmac_wvalid && i_dmac_wready && o_dmac_waddr == 0
+ && o_dmac_wstrb[0] && o_dmac_wdata[0])
+ f_dma_busy <= 1'b1;
+ else if (f_dma_busy)
+ f_dma_busy <= !f_dma_complete;
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (sgstate != SG_WAIT)
+ assert(!f_dma_busy);
+ else if (o_dmac_wvalid && !dma_aborting)
+ assert(!f_dma_busy);
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && S_AXI_ARESETN && $past(f_dma_busy))
+ assert(!dma_starting);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid)
+ begin
+ assume(dma_busy == $past(f_dma_busy));
+ assume(i_dma_complete == $past(f_dma_complete));
+ end
+
+ always @(*)
+ if (!f_dma_busy)
+ assume(!f_dma_complete);
+
+ assume property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ !dma_busy ##1 !dma_busy |-> !$rose(dma_err)
+ );
+
+ assume property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ $rose(dma_busy) |=> !dma_err);
+
+ assume property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ o_dmac_wvalid && i_dmac_wready && o_dmac_wstrb[0]
+ && o_dmac_wdata[4]
+ |=> ##1 !dma_err);
+
+ assume property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ !o_dmac_wvalid || !i_dmac_wready || !o_dmac_wstrb[0]
+ || !o_dmac_wdata[4]
+ |=> ##1 !$fell(dma_err));
+
+
+// assume property (@(posedge S_AXI_ACLK)
+// dma_busy |=> dma_busy [*0:$]
+// ##1 dma_busy && i_dma_complete
+// ##1 !dma_busy);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // State machine checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ always @(*)
+ if (o_new_pc)
+ begin
+ if (C_AXI_ADDR_WIDTH <= 30)
+ begin
+ assert(sgstate == SG_SRCADDR);
+ end else
+ assert(sgstate == SG_SRCHALF);
+ end
+
+ generate if (C_AXI_ADDR_WIDTH > 30)
+ begin
+ always @(*)
+ begin
+ f_tbl_last = (f_tblentry[63:62] == 2'b01);
+ f_tbl_skip = (f_tblentry[63:62] == 2'b10);
+ f_tbl_jump = (f_tblentry[63:62] == 2'b11);
+ f_tbl_int_enable = f_tblentry[127];
+ end
+ end else begin
+ always @(*)
+ begin
+ f_tbl_last = (f_tblentry[31:30] == 2'b01);
+ f_tbl_skip = (f_tblentry[31:30] == 2'b10);
+ f_tbl_jump = (f_tblentry[31:30] == 2'b11);
+ f_tbl_int_enable = f_tblentry[63];
+ end
+ end endgenerate
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && (sgstate != SG_IDLE))
+ begin
+ if ($stable(sgstate))
+ begin
+ assume($stable(f_tblentry));
+ end else if ((C_AXI_ADDR_WIDTH > 30)&&(sgstate != SG_SRCHALF))
+ begin
+ assume($stable(f_tblentry));
+ end else if ((C_AXI_ADDR_WIDTH <= 30)&&(sgstate != SG_SRCADDR))
+ assume($stable(f_tblentry));
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (o_new_pc)
+ f_tbladdr <= o_pf_pc;
+ else if (sgstate == SG_WAIT && dma_op_complete && !dma_terminate)
+ f_tbladdr <= f_tbladdr + TBL_SIZE;
+
+ assert property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ sgstate == SG_IDLE && !i_start
+ |=> sgstate == SG_IDLE && o_pf_clear_cache);
+
+ generate if (C_AXI_ADDR_WIDTH <= 30)
+ begin : SMALL_ADDRESS_SPACE
+ // {{{
+ reg [5:0] f_dma_seq;
+
+ always @(*)
+ begin
+ assert(sgstate != SG_SRCHALF);
+ assert(sgstate != SG_DSTHALF);
+ end
+
+ always @(*)
+ if (i_pf_valid)
+ case(sgstate)
+ // SG_IDLE: begin end
+ // SG_SRCHALF: begin end
+ // SG_DSTHALF: begin end
+ // SG_LENGTH: begin end
+ // SG_CONTROL: begin end
+ // SG_WAIT: begin end
+ SG_SRCADDR: begin
+ assume(i_pf_insn == f_tblentry[31:0]
+ &&(i_pf_pc == f_tbladdr));
+ end
+ SG_DSTADDR: begin
+ assume(i_pf_insn == f_tblentry[63:32]
+ &&(i_pf_pc == f_tbladdr + 4));
+ end
+ SG_LENGTH: begin
+ assume(i_pf_insn == f_tblentry[95:64]
+ &&(i_pf_pc == f_tbladdr + 8));
+ end
+ default: begin end
+ endcase
+
+ assert property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ sgstate == SG_IDLE && i_start
+ |=> o_new_pc && !o_pf_clear_cache
+ && sgstate == SG_SRCADDR
+ && r_pf_pc[C_AXI_ADDR_WIDTH-1:0]
+ == $past(i_tbl_addr));
+
+ initial f_dma_seq = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dma_seq <= 0;
+ else begin
+
+ // SG_SRCADDR, or entering SG_SRCADDR
+ // {{{
+ if ((f_dma_seq == 0)&&(i_start))
+ f_dma_seq <= 1;
+
+ if (f_dma_seq[0] || o_new_pc)
+ begin
+ assert(sgstate == SG_SRCADDR);
+ assert(!o_dmac_wvalid);
+ assert(!dma_busy);
+ f_dma_seq <= 1;
+ if (i_pf_valid && o_pf_ready)
+ begin
+ f_dma_seq <= 2;
+
+ if (f_tbl_jump || f_tbl_skip)
+ f_dma_seq <= 1;
+ end
+
+ if (!o_new_pc)
+ begin
+ assert(o_pf_pc == f_tbladdr);
+ assert(f_pc == o_pf_pc);
+ end
+
+ if ($past(sgstate == SG_SRCADDR
+ && i_pf_valid && o_pf_ready))
+ begin
+ // Jump or skip
+
+ assert(o_new_pc);
+ // Check the jump address
+ // if ($past(i_pf_insn[31:30] == 2'b11))
+ if ($past(f_tbl_jump))
+ begin
+ assert(o_pf_pc == { $past(i_pf_insn[C_AXI_ADDR_WIDTH-1:2]), 2'b00 });
+ end else // Skip
+ assert(o_pf_pc == $past(f_tbladdr + TBL_SIZE));
+ end
+ end
+ // }}}
+
+ // SG_DSTADDR
+ // {{{
+ if (f_dma_seq[1])
+ begin
+ assert(sgstate == SG_DSTADDR);
+ assert(tbl_last == f_tbl_last);
+ assert(o_tbl_addr == f_tbladdr);
+ if ($rose(f_dma_seq[1]))
+ assert(o_dmac_wvalid);
+ assert(o_dmac_waddr == DMA_SRCLO);
+ assert(o_dmac_wdata == f_tblentry[31:0]);
+ assert(&o_dmac_wstrb);
+ assert(!dma_busy);
+ assert(o_pf_pc == f_tbladdr);
+ // assert(f_pc == o_pf_pc + 4);
+ f_dma_seq <= 2;
+ if (i_pf_valid && o_pf_ready)
+ f_dma_seq <= 4;
+ end
+ // }}}
+
+ // SG_LENGTH
+ // {{{
+ if (f_dma_seq[2])
+ begin
+ assert(sgstate == SG_LENGTH);
+ assert(tbl_last == f_tbl_last);
+ assert(tbl_int_enable == f_tbl_int_enable);
+ assert(o_tbl_addr == f_tbladdr);
+ if ($rose(f_dma_seq[2]))
+ assert(o_dmac_wvalid);
+ assert(o_dmac_waddr == DMA_DSTLO);
+ assert(o_dmac_wdata == { 2'b00, f_tblentry[61:32] });
+ assert(&o_dmac_wstrb);
+ assert(!dma_busy);
+ assert(o_pf_pc == f_tbladdr);
+ // assert(f_pc == o_pf_pc + 8);
+ f_dma_seq <= 4;
+ if (i_pf_valid && o_pf_ready)
+ begin
+ if (i_pf_insn == 0)
+ begin
+ if (tbl_last)
+ f_dma_seq <= 0;
+ else
+ f_dma_seq <= 1;
+ end else
+ f_dma_seq <= 8;
+ end
+ end
+ // }}}
+
+ // SG_CONTROL
+ // {{{
+ if (f_dma_seq[3])
+ begin
+ assert(sgstate == SG_CONTROL);
+ assert(tbl_last == f_tbl_last);
+ assert(o_tbl_addr == f_tbladdr);
+ assert(o_dmac_wvalid);
+ assert(o_dmac_waddr == DMA_LENLO);
+ assert(o_dmac_wdata == f_tblentry[95:64]);
+ assert(&o_dmac_wstrb);
+ assert(!dma_busy);
+ assert(o_pf_pc == f_tbladdr);
+ assert(f_pc == f_tbladdr + TBL_SIZE);
+ // assert(f_pc == o_pf_pc);
+ f_dma_seq <= 8;
+ if (i_dmac_wready)
+ f_dma_seq <= 16;
+ end
+ // }}}
+
+ // SG_WAIT && o_dmac_wvalid
+ // {{{
+ if (f_dma_seq[4])
+ begin
+ assert(sgstate == SG_WAIT);
+ assert(tbl_last == f_tbl_last);
+ assert(o_tbl_addr == f_tbladdr + TBL_SIZE);
+ assert(o_dmac_wvalid);
+ assert(o_dmac_waddr == DMA_CONTROL);
+ assert(o_dmac_wdata[15:0] == 16'h1f);
+ assert(&o_dmac_wstrb);
+ assert(!dma_busy);
+ assert(o_pf_pc == f_tbladdr);
+ assert(f_pc == f_tbladdr + TBL_SIZE);
+ // assert(f_pc == o_pf_pc);
+ f_dma_seq <= 16;
+ if (o_dmac_wvalid && i_dmac_wready)
+ f_dma_seq <= 32;
+ end
+ // }}}
+
+ // SG_WAIT && !o_dmac_wvalid
+ // {{{
+ if (f_dma_seq[5])
+ begin
+ assert(sgstate == SG_WAIT);
+ assert(tbl_last == f_tbl_last);
+ assert(o_tbl_addr == f_tbladdr + TBL_SIZE);
+ assert(o_dmac_waddr == DMA_CONTROL);
+ // assert(o_dmac_wdata == f_tblentry[95:64]);
+ if (f_never_abort)
+ begin
+ assert(&o_dmac_wstrb);
+ assert(!o_dmac_wvalid);
+ end else
+ assert(o_dmac_wstrb == 4'h8
+ || o_dmac_wstrb == 4'hf);
+ if (o_dmac_wvalid)
+ assert(!o_dmac_wstrb[0]);
+ f_dma_seq <= 32;
+ assert(o_pf_pc == f_tbladdr);
+ assert(f_pc == f_tbladdr + TBL_SIZE);
+ // assert(f_pc == o_pf_pc);
+ // if (tbl_last || (dma_err && !o_busy)
+ // || i_pf_illegal)
+ if (dma_op_complete)
+ begin
+ if (dma_terminate)
+ // (dma_err && !o_busy))
+ f_dma_seq <= 0;
+ else
+ f_dma_seq <= 1;
+ end
+ end
+ // }}}
+
+ // pf_illegal
+ // {{{
+ if ((|f_dma_seq[2:0]) && i_pf_illegal)
+ f_dma_seq <= 0;
+ // }}}
+
+ // i_abort
+ if ((|f_dma_seq[3:0]) && i_abort
+ && (!o_dmac_wvalid || i_dmac_wready))
+ f_dma_seq <= 0;
+ end
+
+ always @(*)
+ if (f_dma_seq == 0)
+ begin
+ assert(sgstate == SG_IDLE);
+ assert(!o_new_pc);
+ assert(!o_dmac_wvalid);
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin end
+ else if (sgstate == 0)
+ begin
+ assert(o_pf_clear_cache);
+ assert(!dma_busy);
+ end
+
+ cover property (@(posedge S_AXI_ACLK) S_AXI_ARESETN); // !!!
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq == 0 && S_AXI_ARESETN); // !!!
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq == 0 && S_AXI_ARESETN && !i_abort); // !!!
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq == 0 && S_AXI_ARESETN && !i_abort && i_start); // !!!
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq[1]);
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq[2]);
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq[3]);
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq[4]); // !!!
+ cover property (@(posedge S_AXI_ACLK) f_dma_seq[5]); // !!!
+
+// cover property (@(posedge S_AXI_ACLK)
+// disable iff (!S_AXI_ARESETN || i_abort || i_pf_illegal
+// || dma_err)
+// f_dma_seq[0] ##1 1[*0:$] ##1 f_dma_seq[5]
+// ##1 f_dma_seq == 0); // !!!
+
+ cover property (@(posedge S_AXI_ACLK) // !!!
+ f_dma_seq[5] && !tbl_last && f_dma_busy && dma_busy);
+
+ cover property (@(posedge S_AXI_ACLK) // !!!
+ f_dma_seq[5] && tbl_last && f_dma_busy && dma_busy);
+
+ cover property (@(posedge S_AXI_ACLK)
+ f_dma_seq[5] ##2 f_dma_seq[0]); // !!!
+
+ cover property (@(posedge S_AXI_ACLK)
+ f_dma_seq[5] && tbl_last && i_dma_complete); // !!!
+
+ cover property (@(posedge S_AXI_ACLK)
+ f_dma_seq[5] && i_dma_complete); // !!!
+ // }}}
+ end else begin : BIG_ADDR
+ end endgenerate
+
+ always @(*)
+ assert(o_busy == (sgstate != SG_IDLE));
+
+ always @(*)
+ if (o_busy)
+ begin
+ assert(!o_done);
+ assert(!o_err);
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN) && $past(o_busy) && !o_busy)
+ assert(o_done);
+
+// assert property (@(posedge S_AXI_ACLK)
+// !o_done |-> !o_int);
+
+ assert property (@(posedge S_AXI_ACLK)
+ !o_done && !o_busy |-> !o_err);
+
+ always @(*)
+ if (o_pf_ready)
+ assert(!o_dmac_wvalid || i_dmac_wready);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions (i.e. constraints)
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (f_never_abort)
+ assume(!i_abort);
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axispacker.v b/rtl/wb2axip/axispacker.v
new file mode 100644
index 0000000..74b8f5b
--- /dev/null
+++ b/rtl/wb2axip/axispacker.v
@@ -0,0 +1,890 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axispacker
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: AXI-Stream packer: This uses TKEEP to pack bytes in a stream.
+// Bytes with TKEEP clear on input will be removed from the
+// output stream.
+//
+// TID, TDEST, and TUSER fields are not (currently) implemented, although
+// they shouldn't be too hard to add back in.
+//
+// This design *ASSUMES* that TLAST will only be set if TKEEP is not zero.
+// This assumption is not required by the AXI-stream specification, and
+// should be removed in the future.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2021-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axispacker #(
+ parameter C_AXIS_DATA_WIDTH = 32,
+ parameter [0:0] OPT_LOWPOWER = 1
+ ) (
+ // {{{
+ input wire S_AXI_ACLK, S_AXI_ARESETN,
+ // Incoming slave interface
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire [C_AXIS_DATA_WIDTH/8-1:0] S_AXIS_TSTRB,
+ input wire [C_AXIS_DATA_WIDTH/8-1:0] S_AXIS_TKEEP,
+ input wire S_AXIS_TLAST,
+ // }}}
+ // Outgoing AXI-Stream master interface
+ // {{{
+ output reg M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output reg [C_AXIS_DATA_WIDTH/8-1:0] M_AXIS_TSTRB,
+ output reg [C_AXIS_DATA_WIDTH/8-1:0] M_AXIS_TKEEP,
+ output reg M_AXIS_TLAST
+ // }}}
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ localparam DW = C_AXIS_DATA_WIDTH;
+
+ wire pre_tvalid, pre_tready;
+ wire [DW-1:0] pre_tdata;
+ wire [DW/8-1:0] pre_tstrb, pre_tkeep;
+ wire pre_tlast;
+
+ localparam MAX_COUNT = DW/8;
+ localparam COUNT_BITS = $clog2(MAX_COUNT+1)+1;
+
+ reg [DW-1:0] pck_tdata;
+ reg [DW/8-1:0] pck_tstrb, pck_tkeep;
+ reg pck_tlast;
+ reg [COUNT_BITS-1:0] pck_count;
+
+ reg axis_ready;
+
+ wire skd_valid;
+ wire [DW-1:0] skd_data;
+ wire [DW/8-1:0] skd_strb, skd_keep;
+ wire [COUNT_BITS-1:0] skd_count;
+ wire skd_last;
+
+ reg [COUNT_BITS-1:0] mid_fill;
+ reg [DW-1:0] mid_data;
+ reg [DW/8-1:0] mid_strb, mid_keep;
+ reg mid_last;
+
+ reg [2*DW-1:0] w_packed_data;
+ reg [2*DW/8-1:0] w_packed_strb, w_packed_keep;
+
+ localparam TRIM_MAX = MAX_COUNT[COUNT_BITS-1:0];
+ reg next_valid, next_last;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming skid buffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // This makes it so that we can control VALID && DATA (and so
+ // backpressure) with a combinatorial value, something that would
+ // otherwise be against protocol.
+ //
+
+`ifdef FORMAL
+ // {{{
+ assign pre_tvalid = S_AXIS_TVALID;
+ assign S_AXIS_TREADY = pre_tready;
+ assign pre_tdata = S_AXIS_TDATA;
+ assign pre_tstrb = S_AXIS_TSTRB;
+ assign pre_tkeep = S_AXIS_TKEEP;
+ assign pre_tlast = S_AXIS_TLAST;
+ // }}}
+`else
+ skidbuffer #(
+ // {{{
+ .DW(DW + DW/8 + DW/8 + 1),
+ .OPT_OUTREG(1'b1)
+ // }}}
+ ) slave_skd (
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXIS_TVALID), .o_ready(S_AXIS_TREADY),
+ .i_data({ S_AXIS_TDATA, S_AXIS_TSTRB,
+ S_AXIS_TKEEP, S_AXIS_TLAST }),
+ .o_valid(pre_tvalid), .i_ready(pre_tready),
+ .o_data({ pre_tdata, pre_tstrb, pre_tkeep, pre_tlast })
+ // }}}
+ );
+`endif
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Beat packing stage
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ begin
+ { pck_tdata, pck_tstrb, pck_tkeep }
+ = pack( pre_tdata, pre_tstrb,
+ (pre_tvalid) ? pre_tkeep : {(DW/8){1'b0}} );
+ pck_tlast = pre_tlast;
+ // Verilator lint_off WIDTH
+ pck_count = $countones(pre_tkeep);
+ // Verilator lint_on WIDTH
+ end
+
+`ifdef FORMAL
+ // {{{
+ assign skd_valid = pre_tvalid;
+ assign pre_tready = axis_ready;
+ assign skd_data = pck_tdata;
+ assign skd_strb = pck_tstrb;
+ assign skd_keep = pck_tkeep;
+ assign skd_last = pck_tlast;
+ assign skd_count= pck_count;
+ // }}}
+`else
+ skidbuffer #(
+ // {{{
+ .DW(DW + DW/8 + DW/8 + 1 + COUNT_BITS),
+ .OPT_OUTREG(1'b1)
+ // }}}
+ ) beat_skd (
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_reset(!S_AXI_ARESETN),
+ .i_valid(pre_tvalid), .o_ready(pre_tready),
+ .i_data({ pck_count, pck_tdata, pck_tstrb,
+ pck_tkeep, pck_tlast }),
+ .o_valid(skd_valid), .i_ready(axis_ready),
+ .o_data({ skd_count, skd_data, skd_strb, skd_keep,
+ skd_last })
+ // }}}
+ );
+`endif
+
+ function [DW+DW/8+DW/8-1:0] pack; // (i_data, i_strb, i_keep)
+ // {{{
+ input [DW-1:0] i_data;
+ input [DW/8-1:0] i_strb;
+ input [DW/8-1:0] i_keep;
+
+ reg [DW-1:0] p_data;
+ reg [DW/8-1:0] p_strb;
+ reg [DW/8-1:0] p_keep;
+
+ integer rounds, ik;
+ begin
+ p_data = i_data;
+ p_strb = i_strb;
+ p_keep = i_keep;
+ for(ik=0; ik < DW/8; ik=ik+1)
+ begin
+ if (!p_keep[ik])
+ begin
+ p_data[ik*8 +: 8]= 8'h00;
+ p_strb[ik] = 1'b0;
+ end
+ end
+ for(rounds = 0; rounds < DW/8; rounds = rounds+1)
+ begin
+ for(ik=0; ik < DW/8-1; ik=ik+1)
+ begin
+ if (!p_keep[ik])
+ begin
+ p_data[ik*8 +: 8]=p_data[(ik+1)*8 +: 8];
+ p_keep[ik] = p_keep[ik+1];
+ p_strb[ik] = p_strb[ik+1];
+
+ p_keep[ik+1] = 0;
+ p_data[(ik+1)*8 +: 8] = 0;
+ p_strb[ik+1] = 0;
+ end
+ end
+ end
+
+ pack = { p_data, p_strb, p_keep };
+/*
+ p_data = 0;
+ p_strb = 0;
+ p_keep = 0;
+
+ for(fill=0; fill<2*DW; fill=fill+1)
+ begin
+ p_data[(fill *8) +: 8] = i_data[fill*8 +: 8];
+ p_strb[ fill ] = i_strb[fill];
+ p_keep[ fill ] = i_keep[fill];
+ if (fill != 0)
+ p_keep[ fill ] = i_keep[fill]
+ && $countones(i_keep[((fill > 0)?(fill-1):0):0]) == fill;
+ for(ik=fill; ik<2*DW; ik=ik+1)
+ if (i_keep[ik] && $countones(i_keep[ik-1:0]) == fill)
+ begin
+ p_data[fill*8 +: 8] = i_data[ik*8 +: 8];
+ p_strb[fill ] = i_strb[ik];
+ p_keep[fill ] = i_keep[ik];
+ end
+
+ if (!p_keep[fill])
+ begin
+ p_strb[fill] = 1'b0;
+ if (OPT_LOWPOWER)
+ p_data[(fill *8) +: 8] = 8'h00;
+ end
+ end
+
+ pack = { p_data, p_strb, p_keep };
+*/
+ end endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Word Packing stage
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // axis_ready
+ // {{{
+ always @(*)
+ begin
+ axis_ready = 1;
+ if ((mid_last || (skd_count + mid_fill >= TRIM_MAX))
+ &&(M_AXIS_TVALID && !M_AXIS_TREADY))
+ axis_ready = 0;
+ if (!skd_valid)
+ axis_ready = 0;
+ end
+ // }}}
+
+ // mid_fill
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ mid_fill <= 0;
+ else if (mid_last && (!M_AXIS_TVALID || M_AXIS_TREADY))
+ mid_fill <= (axis_ready) ? skd_count : 0;
+ else if (skd_valid && skd_last && (!M_AXIS_TVALID || M_AXIS_TREADY))
+ mid_fill <= (skd_count + mid_fill <= TRIM_MAX) ? 0 : skd_count + mid_fill - TRIM_MAX;
+ else
+ mid_fill <= mid_fill + (axis_ready ? skd_count : 0)
+ - ((next_valid && (!M_AXIS_TVALID || M_AXIS_TREADY))
+ ? TRIM_MAX : 0);
+ // }}}
+
+ // mid_last
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ mid_last <= 0;
+ else if (axis_ready)
+ begin
+ mid_last <= !next_last || (M_AXIS_TVALID && !M_AXIS_TREADY);
+ if (mid_last)
+ mid_last <= 1'b1;
+ if (!skd_last)
+ mid_last <= 1'b0;
+ end else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ mid_last <= 0;
+ // }}}
+
+ // mid_data, mid_strb, mid_keep
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ mid_data <= 0;
+ mid_strb <= 0;
+ mid_keep <= 0;
+ end else if (mid_last && (!M_AXIS_TVALID || M_AXIS_TREADY))
+ begin
+ mid_data <= (axis_ready) ? skd_data : 0;
+ mid_strb <= (axis_ready) ? skd_strb : 0;
+ mid_keep <= (axis_ready) ? skd_keep : 0;
+ end else if (skd_valid && skd_last && (!M_AXIS_TVALID || M_AXIS_TREADY))
+ begin
+ if (skd_count + mid_fill <= TRIM_MAX)
+ begin
+ mid_data <= 0;
+ mid_strb <= 0;
+ mid_keep <= 0;
+ end else begin
+ mid_data <= w_packed_data[2*DW-1:DW];
+ mid_strb <= w_packed_strb[2*DW/8-1:DW/8];
+ mid_keep <= w_packed_keep[2*DW/8-1:DW/8];
+ end
+ end else if (axis_ready)
+ begin
+ if (mid_fill + skd_count >= TRIM_MAX)
+ begin
+ mid_data <= w_packed_data[2*DW-1:DW];
+ mid_strb <= w_packed_strb[2*DW/8-1:DW/8];
+ mid_keep <= w_packed_keep[2*DW/8-1:DW/8];
+ end else begin
+ mid_data <= w_packed_data[DW-1:0];
+ mid_strb <= w_packed_strb[DW/8-1:0];
+ mid_keep <= w_packed_keep[DW/8-1:0];
+ end
+ end
+ // }}}
+
+ // w_packed_data, w_packed_strb, w_packed_keep
+ // {{{
+ always @(*)
+ begin
+ w_packed_data = 0;
+ w_packed_strb = 0;
+
+ w_packed_data = { {(DW){1'b0}}, mid_data }
+ | ( { {(DW){1'b0}}, skd_data } << (mid_fill * 8));
+ w_packed_strb = { {(DW/8){1'b0}}, mid_strb }
+ | ( { {(DW/8){1'b0}}, skd_strb } << (mid_fill));
+ w_packed_keep = { {(DW/8){1'b0}}, mid_keep }
+ | ( { {(DW/8){1'b0}}, skd_keep } << (mid_fill));
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // next_valid
+ // {{{
+ always @(*)
+ begin
+ next_valid = skd_valid && (skd_count + mid_fill >= TRIM_MAX);
+ if (mid_last || (skd_last && skd_valid))
+ next_valid = 1;
+ if (skd_count + mid_fill == 0)
+ next_valid = 0;
+ end
+ // }}}
+
+ // next_last
+ // {{{
+ always @(*)
+ begin
+ next_last = mid_last;
+ if (skd_valid && skd_last && (skd_count + mid_fill <= TRIM_MAX))
+ next_last = 1;
+ if (!next_valid)
+ next_last = 0;
+ end
+ // }}}
+
+ // M_AXIS_TVALID
+ // {{{
+ initial M_AXIS_TVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXIS_TVALID <= 0;
+ else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ M_AXIS_TVALID <= next_valid;
+ // }}}
+
+ // M_AXIS_TDATA, M_AXIS_TSTRB, M_AXIS_TKEEP, M_AXIS_TLAST
+ // {{{
+ initial M_AXIS_TDATA = 0;
+ initial M_AXIS_TSTRB = 0;
+ initial M_AXIS_TKEEP = 0;
+ initial M_AXIS_TLAST = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ M_AXIS_TDATA <= 0;
+ M_AXIS_TSTRB <= 0;
+ M_AXIS_TKEEP <= 0;
+ M_AXIS_TLAST <= 0;
+ end else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ begin
+ if (mid_last)
+ begin
+ M_AXIS_TDATA <= mid_data;
+ M_AXIS_TSTRB <= mid_strb;
+ M_AXIS_TKEEP <= mid_keep;
+ M_AXIS_TLAST <= 1'b1;
+ end else begin
+ M_AXIS_TDATA <= w_packed_data[DW-1:0];
+ M_AXIS_TSTRB <= w_packed_strb[DW/8-1:0];
+ M_AXIS_TKEEP <= w_packed_keep[DW/8-1:0];
+ M_AXIS_TLAST <= next_last;
+ end
+
+
+ if (OPT_LOWPOWER && !next_valid)
+ begin
+ M_AXIS_TDATA <= 0;
+ M_AXIS_TSTRB <= 0;
+ M_AXIS_TKEEP <= 0;
+ M_AXIS_TLAST <= 0;
+ end
+ end
+ // }}}
+
+ // }}}
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ localparam F_COUNT = COUNT_BITS + 4 + 16;
+ reg [F_COUNT-1:0] f_icount, f_ocount, f_ibeat, f_obeat,
+ f_mcount, f_mbeat;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming stream assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assume(!S_AXIS_TVALID);
+ end else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ begin
+ assume(S_AXIS_TVALID);
+ assume($stable(S_AXIS_TDATA));
+ assume($stable(S_AXIS_TSTRB));
+ assume($stable(S_AXIS_TKEEP));
+ assume($stable(S_AXIS_TLAST));
+ end
+
+ // If TKEEP is low, TSTRB must be low as well
+ always @(*)
+ if (S_AXI_ARESETN)
+ assume(((~S_AXIS_TKEEP) & S_AXIS_TSTRB) == 0);
+
+ // TLAST requires at least one beat
+ always @(*)
+ if (S_AXI_ARESETN && S_AXIS_TVALID && S_AXIS_TLAST)
+ assume(S_AXIS_TKEEP != 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing stream assertions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(!M_AXIS_TVALID);
+ end else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ begin
+ assert(M_AXIS_TVALID);
+ assert($stable(M_AXIS_TDATA));
+ assert($stable(M_AXIS_TSTRB));
+ assert($stable(M_AXIS_TKEEP));
+ assert($stable(M_AXIS_TLAST));
+ end
+
+ // If TKEEP is low, TSTRB must be low as well
+ always @(*)
+ if (S_AXI_ARESETN && M_AXIS_TVALID)
+ assert(((~M_AXIS_TKEEP) & M_AXIS_TSTRB) == 0);
+
+ // Our output requirement is that if TLAST is ever low, TKEEP must
+ // be all ones
+ always @(*)
+ if (S_AXI_ARESETN && M_AXIS_TVALID && !M_AXIS_TLAST)
+ assert(&M_AXIS_TKEEP);
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_LOWPOWER && !M_AXIS_TVALID)
+ begin
+ assert(M_AXIS_TDATA == 0);
+ assert(M_AXIS_TSTRB == 0);
+ assert(M_AXIS_TKEEP == 0);
+ assert(M_AXIS_TLAST == 0);
+ end
+
+ // TLAST requires at least one beat
+ always @(*)
+ if (S_AXI_ARESETN && M_AXIS_TVALID && M_AXIS_TLAST)
+ assert(M_AXIS_TKEEP != 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction assertions for the middle
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ genvar gk;
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(mid_fill == { 1'b0, $countones(mid_keep) });
+
+ always @(*)
+ if (S_AXI_ARESETN && mid_last)
+ assert(mid_fill > 0);
+
+ // If TKEEP is low, TSTRB must be low as well
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (mid_fill > 0)
+ begin
+ assert(((~mid_keep) & mid_strb) == 0);
+ end else begin
+ // assert(mid_data == 0);
+ assert(mid_strb == 0);
+ end
+ end
+
+ // Our output requirement is that if TLAST is ever low, TKEEP must
+ // be all ones
+ generate for(gk=1; gk<DW/8; gk=gk+1)
+ begin
+ always @(*)
+ if (S_AXI_ARESETN && mid_fill > 0 && mid_keep[gk])
+ assert(&mid_keep[gk-1:0]);
+ end endgenerate
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_LOWPOWER && !M_AXIS_TVALID)
+ begin
+ assert(M_AXIS_TDATA == 0);
+ assert(M_AXIS_TSTRB == 0);
+ assert(M_AXIS_TKEEP == 0);
+ assert(M_AXIS_TLAST == 0);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Pick a byte of the given packet. Force that byte to have a known
+ // value. Prove that the same byte on the output has the same known
+ // value.
+
+ (* anyconst *) reg [F_COUNT-1:0] fc_count;
+ (* anyconst *) reg [8-1:0] fc_data;
+ (* anyconst *) reg fc_strb, fc_last;
+ wire [F_COUNT:0] f_chk_count;
+
+ // Assume the special input
+ // {{{
+ generate for(gk=0; gk<DW/8; gk=gk+1)
+ begin
+ if (gk == 0)
+ begin
+ always @(*)
+ if (S_AXIS_TVALID && S_AXIS_TKEEP[0]
+ && f_icount == fc_count)
+ begin
+ assume(fc_data == S_AXIS_TDATA[7:0]);
+ assume(fc_strb == S_AXIS_TSTRB[0]);
+ if (fc_last)
+ begin
+ assume(S_AXIS_TKEEP[DW/8-1:1] == 0);
+ assume(S_AXIS_TLAST);
+ end else if (S_AXIS_TLAST)
+ assume(S_AXIS_TKEEP[DW/8-1:1] != 0);
+ end
+ end else begin
+
+ always @(*)
+ if (S_AXIS_TKEEP[gk] && (f_icount
+ // Verilator lint_off WIDTH
+ + $countones(S_AXIS_TKEEP[gk-1:0]) == fc_count))
+ // Verilator lint_on WIDTH
+ begin
+ assume(S_AXIS_TDATA[gk*8 +: 8] == fc_data);
+ assume(S_AXIS_TSTRB[gk] == fc_strb);
+ if (fc_last)
+ begin
+ if (gk < DW/8-1)
+ assume(S_AXIS_TKEEP[DW/8-1:gk+1] == 0);
+ assume(S_AXIS_TLAST);
+ end else if (gk < DW/8-1)
+ begin
+ if (S_AXIS_TLAST)
+ assume(S_AXIS_TKEEP[DW/8-1:gk+1] != 0);
+ end else
+ assume(!S_AXIS_TLAST);
+ end
+ end
+ end endgenerate
+ // }}}
+
+ // Assert the special output
+ // {{{
+ generate for(gk=0; gk<DW/8; gk=gk+1)
+ begin
+ if (gk == 0)
+ begin
+ always @(*)
+ if (S_AXI_ARESETN && M_AXIS_TVALID && M_AXIS_TKEEP[0]
+ && f_ocount == fc_count)
+ begin
+ assert(M_AXIS_TDATA[7:0] == fc_data);
+ assert(M_AXIS_TSTRB[0] == fc_strb);
+ if (fc_last)
+ begin
+ assert(M_AXIS_TKEEP[DW/8-1:1] == 0);
+ assert(M_AXIS_TLAST);
+ end else if (M_AXIS_TLAST)
+ assert(M_AXIS_TKEEP[DW/8-1:1] != 0);
+ end
+ end else begin
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXIS_TKEEP[gk] &&
+ // Verilator lint_off WIDTH
+ (f_ocount + $countones(M_AXIS_TKEEP[gk-1:0])
+ == fc_count))
+ // Verilator lint_on WIDTH
+ begin
+ assert(M_AXIS_TDATA[gk*8 +: 8] == fc_data);
+ assert(M_AXIS_TSTRB[gk] == fc_strb);
+ if (fc_last)
+ begin
+ if (gk < DW/8-1)
+ assert(M_AXIS_TKEEP[DW/8-1:gk+1] == 0);
+ assert(M_AXIS_TLAST);
+ end else if (gk < DW/8-1)
+ begin
+ if (M_AXIS_TLAST)
+ assert(M_AXIS_TKEEP[DW/8-1:gk+1] != 0);
+ end else
+ assert(!M_AXIS_TLAST);
+ end
+ end
+ end endgenerate
+ // }}}
+
+ // f_icount
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_icount <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ // Verilator lint_off WIDTH
+ f_icount <= f_icount + $countones(S_AXIS_TKEEP);
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // f_ocount
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_ocount <= 0;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ // Verilator lint_off WIDTH
+ f_ocount <= f_ocount + $countones(M_AXIS_TKEEP);
+ // Verilator lint_on WIDTH
+ // }}}
+
+ // Induction assertion(s) on mid_*
+ // {{{
+ always @(*)
+ // Verilator lint_off WIDTH
+ f_mcount = f_icount - mid_fill;
+ // Verilator lint_on WIDTH
+
+ generate for(gk=0; gk<DW/8; gk=gk+1)
+ begin
+ if (gk == 0)
+ begin
+ always @(*)
+ if (S_AXI_ARESETN && mid_fill > 0
+ && f_mcount == fc_count)
+ begin
+ assert(mid_data[7:0] == fc_data);
+ assert(mid_strb[0] == fc_strb);
+ assert(mid_keep[0]);
+ if (fc_last)
+ begin
+ assert(mid_fill == 1);
+ assert(mid_last);
+ end else if (mid_last)
+ assert(mid_fill > 1);
+ end else if (S_AXI_ARESETN && mid_fill == 0)
+ begin
+ assert(mid_data[7:0] == 8'h00);
+ assert(!mid_strb[0]);
+ assert(!mid_keep[0]);
+ end
+ end else begin
+
+ always @(*)
+ if (S_AXI_ARESETN && mid_fill > gk
+ && (f_mcount + gk == fc_count))
+ begin
+ assert(mid_data[gk*8 +: 8] == fc_data);
+ assert(mid_strb[gk] == fc_strb);
+ assert(mid_keep[gk]);
+ if (fc_last)
+ begin
+ assert(mid_fill == gk + 1);
+ assert(mid_last);
+ end else if (gk < DW/8-1)
+ begin
+ if (mid_last)
+ assert(mid_fill > gk + 1);
+ end else
+ assert(!mid_last);
+ end else if (S_AXI_ARESETN && mid_fill <= gk)
+ begin
+ assert(mid_data[gk*8 +: 8] == 8'h00);
+ assert(!mid_strb[gk]);
+ assert(!mid_keep[gk]);
+ end
+ end
+ end endgenerate
+ // }}}
+
+ // Relate icount to ocount
+ // {{{
+ // Verilator lint_off WIDTH
+ assign f_chk_count = f_ocount + mid_fill + $countones(M_AXIS_TKEEP);
+ // Verilator lint_on WIDTH
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assume(f_chk_count[F_COUNT] == 1'b0);
+ assert(f_icount == f_chk_count[F_COUNT-1:0]);
+ end
+ // }}}
+
+ // f_ibeat
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_ibeat <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ begin
+ // Verilator lint_off WIDTH
+ f_ibeat <= f_ibeat + $countones(S_AXIS_TKEEP);
+ // Verilator lint_on WIDTH
+ if (M_AXIS_TLAST)
+ f_ibeat <= 0;
+ end
+ // }}}
+
+ // f_obeat
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_obeat <= 0;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ begin
+ // Verilator lint_off WIDTH
+ f_obeat <= f_obeat + $countones(M_AXIS_TKEEP);
+ // Verilator lint_on WIDTH
+ if (M_AXIS_TLAST)
+ f_obeat <= 0;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ cover(M_AXIS_TVALID && M_AXIS_TREADY && M_AXIS_TLAST
+ && $past(M_AXIS_TVALID&& M_AXIS_TREADY&& M_AXIS_TLAST));
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ cover(M_AXIS_TVALID && !M_AXIS_TLAST
+ && $past(M_AXIS_TVALID&& M_AXIS_TREADY&& M_AXIS_TLAST));
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ cover(M_AXIS_TVALID && M_AXIS_TLAST
+ && $past(M_AXIS_TVALID&& M_AXIS_TREADY&&!M_AXIS_TLAST));
+
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_formal;
+ assign unused_formal = &{ 1'b0, f_ibeat, f_obeat, f_icount, f_ocount };
+ // Verilator lint_on UNUSED
+ // }}}
+`endif // FORMAL
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axisrandom.v b/rtl/wb2axip/axisrandom.v
new file mode 100644
index 0000000..43d91f9
--- /dev/null
+++ b/rtl/wb2axip/axisrandom.v
@@ -0,0 +1,136 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisrandom
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: An AXI-stream based pseudorandom noise generator
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+//
+`default_nettype none
+//
+module axisrandom #(
+ // {{{
+ localparam C_AXIS_DATA_WIDTH = 32
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ output reg M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA
+ // }}}
+ );
+
+ localparam INITIAL_FILL = { 1'b1, {(C_AXIS_DATA_WIDTH-1){1'b0}} };
+ localparam LGPOLY = 31;
+ localparam [LGPOLY-1:0] CORE_POLY = { 31'h00_00_20_01 };
+ localparam [C_AXIS_DATA_WIDTH-1:0] POLY
+ = { CORE_POLY, {(C_AXIS_DATA_WIDTH-31){1'b0}} };
+
+ // M_AXIS_TVALID
+ // {{{
+ initial M_AXIS_TVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXIS_TVALID <= 1'b0;
+ else
+ M_AXIS_TVALID <= 1'b1;
+ // }}}
+
+ // M_AXIS_TDATA
+ // {{{
+ //
+ // Note--this setup is *FAR* from cryptographically random.
+ //
+ initial M_AXIS_TDATA = INITIAL_FILL;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXIS_TDATA <= INITIAL_FILL;
+ else if (M_AXIS_TREADY)
+ begin
+ M_AXIS_TDATA <= M_AXIS_TDATA >> 1;
+ M_AXIS_TDATA[C_AXIS_DATA_WIDTH-1] <= ^(M_AXIS_TDATA & POLY);
+ end
+ // }}}
+
+ // Verilator lint_off UNUSED
+ // {{{
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used in verfiying this core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ // Make certain this polynomial will never degenerate, and so that
+ // this random number stream will go on for ever--eventually repeating
+ // after 2^LGPOLY-1 (hopefully) elements.
+ always @(*)
+ assert(M_AXIS_TDATA[C_AXIS_DATA_WIDTH-1
+ :C_AXIS_DATA_WIDTH-LGPOLY] != 0);
+
+ // AXI stream has only one significant property
+ // {{{
+ // Here we'll modify it slightly for our purposes
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ assert(!M_AXIS_TVALID);
+ else begin
+ assert(M_AXIS_TVALID);
+ if ($past(M_AXIS_TVALID && !M_AXIS_TREADY))
+ // Normally I'd assesrt M_AXIS_TVALID here, not above,
+ // but this core *ALWAYS* produces data
+ assert($stable(M_AXIS_TDATA));
+ else if ($past(M_AXIS_TVALID))
+ // Insist that the data always changes otherwise
+ assert($changed(M_AXIS_TDATA));
+ end
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axissafety.v b/rtl/wb2axip/axissafety.v
new file mode 100644
index 0000000..14a194d
--- /dev/null
+++ b/rtl/wb2axip/axissafety.v
@@ -0,0 +1,677 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axissafety.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A firewall for the AXI-stream protocol. Only handles TVALID,
+// TREADY, TDATA, TLAST and TUSER signals.
+//
+// This module is designed to be a "bump in the stream" of the stream
+// protocol--a bump that can be used to detect AXI stream errors.
+// Specifically, this firewall can detect one of three errors:
+//
+// 1. CHANGE The stream is stalled and something changes. This
+// could easily be caused by a stream overflow.
+// 2. PACKET LENGTH If OPT_PACKET_LENGTH != 0, then all stream
+// packets are assumed to be OPT_PACKET_LENGTH in length.
+// Packets that are not OPT_PACKET_LENGTH in length will
+// generate a fault.
+//
+// This core cannot handle dynamic packet lengths, but
+// should do just fine with detecting errors in fixed
+// length packets--such as an FFT might use.
+// 3. TOO MANY STALLS
+// If OPT_MAX_STALL != 0, and the master stalls an input more than
+// OPT_MAX_STALL cycles, then a fault will be declared.
+//
+// 4. (Sorry--I haven't implemented a video firewall. Video images
+// having TUSER set to start of frame will not be guaranteed
+// video stream compliant, since TUSER might still be set to the
+// wrong number.)
+//
+// If a fault is detected, o_fault will be set true. This is designed so
+// that you can trigger an ILA off of a protocol error, and then see what
+// caused the fault.
+//
+// If OPT_SELF_RESET is set, then o_fault will be self clearing. Once
+// a fault is detected, the current packet will be flushed, and the
+// firewall will become idle (holding TREADY high) until S_AXIS_TVALID
+// and S_AXIS_TLAST--marking the end of a broken packet. This will
+// resynchronize the core, following which packets may pass through again.
+//
+// If you aren't using TLAST, set it to one.
+//
+// If you aren't using TUSER, set the width to 1 and the input to a
+// constant zero.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+//
+// }}}
+module axissafety #(
+ // {{{
+`ifdef FORMAL
+ parameter [0:0] F_OPT_FAULTLESS = 0,
+`endif
+ parameter C_AXIS_DATA_WIDTH = 8,
+ parameter C_AXIS_USER_WIDTH = 1,
+ parameter OPT_MAX_STALL = 0,
+ parameter OPT_PACKET_LENGTH = 0,
+ parameter [0:0] OPT_SELF_RESET = 0
+ // }}}
+ ) (
+ // {{{
+ output reg o_fault,
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI-stream slave (incoming) port
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire S_AXIS_TLAST, // = 1,
+ input wire [C_AXIS_USER_WIDTH-1:0] S_AXIS_TUSER, // = 0,
+ // }}}
+ // AXI-stream master (outgoing) port
+ // {{{
+ output reg M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output reg M_AXIS_TLAST,
+ output reg [C_AXIS_USER_WIDTH-1:0] M_AXIS_TUSER
+ // }}}
+ // }}}
+ );
+
+ // Parameter/register declarations
+ // {{{
+ localparam LGPKTLEN = $clog2(OPT_PACKET_LENGTH+1);
+ localparam LGSTALLCOUNT = $clog2(OPT_MAX_STALL+1);
+ reg skdr_valid, skd_valid, skd_ready;
+
+ reg [C_AXIS_DATA_WIDTH+1+C_AXIS_USER_WIDTH-1:0] idata,skdr_data;
+ reg [C_AXIS_DATA_WIDTH-1:0] skd_data;
+ reg [C_AXIS_USER_WIDTH-1:0] skd_user;
+ reg skd_last;
+
+ reg r_stalled;
+ reg packet_fault, change_fault, stall_fault;
+ reg [C_AXIS_DATA_WIDTH+1+C_AXIS_USER_WIDTH-1:0] past_data;
+
+ reg [LGSTALLCOUNT-1:0] stall_count;
+ reg [LGPKTLEN-1:0] s_packet_counter;
+ reg [LGPKTLEN-1:0] m_packet_count;
+
+ reg m_end_of_packet, m_idle;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Implement a skid buffer with no assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ idata = { S_AXIS_TUSER, S_AXIS_TLAST, S_AXIS_TDATA };
+
+ initial skdr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ skdr_valid <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY && (skd_valid && !skd_ready))
+ skdr_valid <= 1;
+ else if (skd_ready)
+ skdr_valid <= 0;
+
+ initial skdr_data = 0; // Allow data to be optimized away if always clr
+ always @(posedge S_AXI_ACLK)
+ if (S_AXIS_TVALID && S_AXIS_TREADY)
+ skdr_data <= idata;
+
+ always @(*)
+ if (S_AXIS_TREADY)
+ { skd_user, skd_last, skd_data } = idata;
+ else
+ { skd_user, skd_last, skd_data } = skdr_data;
+
+ always @(*)
+ skd_valid = S_AXIS_TVALID || skdr_valid;
+
+ assign S_AXIS_TREADY = !skdr_valid;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Fault detection
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // r_stalled
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_stalled <= 0;
+ else
+ r_stalled <= (S_AXIS_TVALID && !S_AXIS_TREADY);
+ // }}}
+
+ // past_data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ past_data <= idata;
+ // }}}
+
+ always @(*)
+ change_fault = (r_stalled && (past_data != idata));
+
+ // packet_fault
+ // {{{
+ generate if (OPT_PACKET_LENGTH != 0)
+ begin : S_PACKET_COUNT
+ // {{{
+
+ // s_packet_counter
+ // {{{
+ initial s_packet_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_packet_counter <= 0;
+ else if (o_fault)
+ s_packet_counter <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ begin
+ s_packet_counter <= s_packet_counter + 1;
+ if (S_AXIS_TLAST)
+ s_packet_counter <= 0;
+ end
+ // }}}
+
+ // packet_fault
+ // {{{
+ always @(*)
+ begin
+ packet_fault = (S_AXIS_TLAST
+ != (s_packet_counter == OPT_PACKET_LENGTH-1));
+ if (!S_AXIS_TVALID)
+ packet_fault = 0;
+ end
+ // }}}
+ // }}}
+ end else begin
+ // {{{
+ always @(*)
+ packet_fault = 1'b0;
+ always @(*)
+ s_packet_counter = 0;
+
+ // Verilator lint_off UNUSED
+ wire unused_pkt_counter;
+ assign unused_pkt_counter = &{ 1'b0, s_packet_counter };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+
+ // stall_fault
+ // {{{
+ generate if (OPT_MAX_STALL != 0)
+ begin : S_STALL_COUNT
+ // {{{
+
+ initial stall_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ stall_count <= 0;
+ else if (!S_AXIS_TVALID || S_AXIS_TREADY)
+ stall_count <= 0;
+ else
+ stall_count <= stall_count + 1;
+
+ always @(*)
+ stall_fault = (stall_count > OPT_MAX_STALL);
+ // }}}
+ end else begin
+ // {{{
+ always @(*)
+ stall_fault = 0;
+
+ always @(*)
+ stall_count = 0;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // o_fault
+ // {{{
+ initial o_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ o_fault <= 0;
+ else if (!o_fault)
+ o_fault <= change_fault || stall_fault || packet_fault;
+ else if (OPT_SELF_RESET)
+ begin
+ if (skd_last && skd_ready && m_idle)
+ o_fault <= 1'b0;
+ end
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The outgoing AXI-stream port
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // skd_ready
+ // {{{
+ always @(*)
+ if (!o_fault && !change_fault && !stall_fault && !packet_fault)
+ skd_ready = !M_AXIS_TVALID || M_AXIS_TREADY;
+ else if (!o_fault)
+ skd_ready = 1'b0;
+ else
+ skd_ready = 1'b1;
+ // }}}
+
+ // m_end_of_packet, m_packet_count
+ // {{{
+ generate if (OPT_PACKET_LENGTH != 0)
+ begin
+ // {{{
+ initial m_packet_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_packet_count <= 0;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ begin
+ m_packet_count <= m_packet_count + 1;
+ if (M_AXIS_TLAST)
+ m_packet_count <= 0;
+ end
+
+ // initial m_end_of_packet = 0;
+ // always @(posedge S_AXI_ACLK)
+ // if (!S_AXI_ARESETN)
+ // m_end_of_packet <= 0;
+ // else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ // m_end_of_packet <= (m_packet_count >= (OPT_PACKET_LENGTH-3));
+ always @(*)
+ begin
+ m_end_of_packet = (m_packet_count == (OPT_PACKET_LENGTH-1));
+ if (M_AXIS_TVALID)
+ m_end_of_packet = (m_packet_count == (OPT_PACKET_LENGTH-2));
+ end
+ // }}}
+ end else begin
+ // {{{
+ always @(*)
+ m_end_of_packet = 1'b1;
+ always @(*)
+ m_packet_count = 0;
+ // Verilator lint_off UNUSED
+ wire unused_mpkt_counter;
+ assign unused_mpkt_counter = &{ 1'b0, m_packet_count };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+
+ initial m_idle = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ m_idle <= 1;
+ else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ begin
+ if (M_AXIS_TVALID && M_AXIS_TLAST)
+ m_idle <= o_fault || (!skd_valid || !skd_ready);
+ else if (m_idle && !o_fault)
+ m_idle <= (!skd_valid || !skd_ready);
+ end
+ // }}}
+
+ // M_AXIS_TVALID
+ // {{{
+ initial M_AXIS_TVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXIS_TVALID <= 1'b0;
+ else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ begin
+ M_AXIS_TVALID <= 1'b0;
+ if (!o_fault && skd_ready)
+ M_AXIS_TVALID <= skd_valid;
+ else if (o_fault)
+ begin
+ if (m_idle)
+ M_AXIS_TVALID <= 1'b0;
+ else if (!M_AXIS_TVALID || M_AXIS_TLAST)
+ M_AXIS_TVALID <= 1'b0;
+ else
+ M_AXIS_TVALID <= 1'b1;
+ end
+ end
+ // }}}
+
+ // M_AXIS_TUSER, M_AXIS_TLAST, M_AXIS_TDATA
+ // {{{
+ initial { M_AXIS_TUSER, M_AXIS_TLAST, M_AXIS_TDATA } <= 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ begin
+ if (o_fault || change_fault || packet_fault)
+ begin
+ { M_AXIS_TUSER, M_AXIS_TLAST, M_AXIS_TDATA } <= 0;
+ // if (!M_AXIS_TLAST)
+ // M_AXIS_TLAST <= m_end_of_packet;
+ end else
+ { M_AXIS_TUSER, M_AXIS_TLAST, M_AXIS_TDATA }
+ <= { skd_user, skd_last, skd_data };
+
+ if (OPT_PACKET_LENGTH != 0)
+ M_AXIS_TLAST <= m_end_of_packet;
+ else if (o_fault && !m_idle)
+ M_AXIS_TLAST <= 1'b1;
+ end
+
+ if (!S_AXI_ARESETN)
+ M_AXIS_TLAST <= 0;
+ end
+ // }}}
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ reg [LGPKTLEN-1:0] fm_packet_counter;
+ reg [LGSTALLCOUNT-1:0] fm_stall_count;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ // Stability
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || !$past(S_AXI_ARESETN))
+ assert(!M_AXIS_TVALID);
+ else if ($past(M_AXIS_TVALID && !M_AXIS_TREADY))
+ begin
+ assert(M_AXIS_TVALID);
+ assert($stable(M_AXIS_TDATA));
+ assert($stable(M_AXIS_TLAST));
+ assert($stable(M_AXIS_TUSER));
+ end
+ // }}}
+
+ // Packet counter, assertions on the output
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ fm_packet_counter <= 0;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ begin
+ fm_packet_counter <= fm_packet_counter + 1;
+ if (M_AXIS_TLAST)
+ fm_packet_counter <= 0;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_PACKET_LENGTH != 0)
+ begin
+ assert(fm_packet_counter < OPT_PACKET_LENGTH);
+ assert(M_AXIS_TLAST == (fm_packet_counter == OPT_PACKET_LENGTH-1));
+
+ assert(m_packet_count == fm_packet_counter);
+ if (!o_fault)
+ begin
+ if (skdr_valid && skd_last)
+ begin
+ assert(s_packet_counter == 0);
+ assert(m_packet_count == OPT_PACKET_LENGTH-2);
+ end else if (M_AXIS_TVALID && M_AXIS_TLAST)
+ begin
+ assert(s_packet_counter == (skdr_valid ? 1:0));
+ assert(m_packet_count == OPT_PACKET_LENGTH-1);
+ end else
+ assert(s_packet_counter == m_packet_count
+ + (skdr_valid ? 1:0)
+ + (M_AXIS_TVALID ? 1:0));
+ end
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_fault && OPT_PACKET_LENGTH > 0)
+ assert(s_packet_counter < OPT_PACKET_LENGTH);
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_PACKET_LENGTH > 0)
+ assert(m_idle == (m_packet_count == 0 && !M_AXIS_TVALID));
+ // }}}
+
+ // Input stall counting
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ fm_stall_count <= 0;
+ else if (!S_AXIS_TVALID || S_AXIS_TREADY)
+ fm_stall_count <= 0;
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_MAX_STALL > 0)
+ assert(fm_stall_count < OPT_MAX_STALL);
+ // }}}
+
+ generate if (F_OPT_FAULTLESS)
+ begin
+ // {{{
+ // Stall and stability properties
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || !$past(S_AXI_ARESETN))
+ assume(!S_AXIS_TVALID);
+ else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ begin
+ assume(S_AXIS_TVALID);
+ assume($stable(S_AXIS_TDATA));
+ assume($stable(S_AXIS_TLAST));
+ assume($stable(S_AXIS_TUSER));
+ end
+ // }}}
+
+ // f_packet_counter
+ // {{{
+ if (OPT_PACKET_LENGTH != 0)
+ begin
+ // {{{
+ reg [LGPKTLEN-1:0] fs_packet_counter;
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ fs_packet_counter <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ begin
+ fs_packet_counter <= fs_packet_counter + 1;
+ if (S_AXIS_TLAST)
+ fs_packet_counter <= 0;
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ assert(s_packet_counter == fs_packet_counter);
+ if (skdr_valid && skd_last)
+ assert(s_packet_counter == 0);
+ else if (M_AXIS_TVALID && M_AXIS_TLAST)
+ assert(s_packet_counter == (skdr_valid ? 1:0));
+ else // if (!M_AXIS_TVALID && !M_AXIS_TLAST)
+ assert(s_packet_counter
+ == fm_packet_counter
+ + (skdr_valid ? 1:0)
+ + (M_AXIS_TVALID ? 1:0));
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && S_AXIS_TVALID)
+ assume(S_AXIS_TLAST == (fs_packet_counter == OPT_PACKET_LENGTH-1));
+ // }}}
+ end
+ // }}}
+
+ // f_stall_count
+ // {{{
+ if (OPT_MAX_STALL != 0)
+ begin
+ // {{{
+ reg [LGSTALLCOUNT-1:0] f_stall_count;
+
+ initial stall_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_stall_count <= 0;
+ else if (!S_AXIS_TVALID || S_AXIS_TREADY)
+ f_stall_count <= 0;
+ else
+ f_stall_count <= f_stall_count + 1;
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(stall_count == f_stall_count);
+
+ always @(*)
+ assume(f_stall_count <= OPT_MAX_STALL);
+ // }}}
+ end
+ // }}}
+
+ always @(*)
+ assert(!o_fault);
+ // }}}
+ end endgenerate
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+
+ generate if (F_OPT_FAULTLESS)
+ begin
+ end else begin
+
+ reg cvr_stall_fault, cvr_packet_fault, cvr_change_fault,
+ cvr_last;
+
+ initial cvr_last = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_last <= 1'b0;
+ else if (o_fault && $stable(o_fault)
+ && $rose(M_AXIS_TVALID && M_AXIS_TLAST))
+ cvr_last <= 1'b1;
+
+ initial cvr_change_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_change_fault <= 1'b0;
+ else if (!o_fault && change_fault && !packet_fault && !stall_fault)
+ cvr_change_fault <= 1'b1;
+ else if (!o_fault)
+ cvr_change_fault <= 1'b0;
+
+ if (OPT_PACKET_LENGTH > 0)
+ begin
+ initial cvr_packet_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_packet_fault <= 1'b0;
+ else if (!o_fault && !change_fault && packet_fault && !stall_fault)
+ cvr_packet_fault <= 1'b1;
+ else if (!o_fault)
+ cvr_packet_fault <= 1'b0;
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ begin
+ cover($fell(o_fault) && cvr_packet_fault);
+ cover($fell(o_fault) && cvr_packet_fault && cvr_last);
+ end
+ end
+
+ if (OPT_MAX_STALL > 0)
+ begin
+ initial cvr_stall_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ cvr_stall_fault <= 1'b0;
+ else if (!o_fault && !change_fault && !packet_fault && stall_fault)
+ cvr_stall_fault <= 1'b1;
+ else if (!o_fault)
+ cvr_stall_fault <= 1'b0;
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ begin
+ cover($fell(o_fault) && cvr_stall_fault);
+ cover($fell(o_fault) && cvr_stall_fault && cvr_last);
+ end
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && $past(S_AXI_ARESETN))
+ begin
+ cover($fell(o_fault));
+ cover($fell(o_fault) && cvr_change_fault);
+ cover($fell(o_fault) && cvr_change_fault && cvr_last);
+ end
+
+ end endgenerate
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axisswitch.v b/rtl/wb2axip/axisswitch.v
new file mode 100644
index 0000000..bcae4a3
--- /dev/null
+++ b/rtl/wb2axip/axisswitch.v
@@ -0,0 +1,631 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisswitch.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Switch from among several AXI streams based upon an AXI-lite
+// controlled index. All streams must have the same width.
+// The switch will use TLAST to guarantee that it will not change
+// mid-packet. If TLAST is unused for a particular input, simply set it
+// to 1'b1.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axisswitch #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have a single configuration words.
+ parameter C_AXI_ADDR_WIDTH = 2,
+ localparam C_AXI_DATA_WIDTH = 32,
+ //
+ parameter NUM_STREAMS = 4,
+ parameter C_AXIS_DATA_WIDTH = 32,
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI-Lite control
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ // }}}
+ // AXI stream inputs to be switched
+ // {{{
+ input wire [NUM_STREAMS-1:0] S_AXIS_TVALID,
+ output wire [NUM_STREAMS-1:0] S_AXIS_TREADY,
+ input wire [NUM_STREAMS*C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire [NUM_STREAMS-1:0] S_AXIS_TLAST,
+ // }}}
+ // AXI stream output result
+ // {{{
+ output reg M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output reg M_AXIS_TLAST
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ localparam LGNS = $clog2(NUM_STREAMS);
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ wire [0:0] awskd_addr; // UNUSED
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ wire [0:0] arskd_addr; // UNUSED
+ reg [C_AXI_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ reg [LGNS-1:0] r_index;
+ wire [31:0] wskd_index;
+
+ genvar gk;
+ reg [NUM_STREAMS-1:0] skd_switch_ready;
+ reg [LGNS-1:0] switch_index;
+ wire [C_AXIS_DATA_WIDTH-1:0] skd_data [0:NUM_STREAMS-1];
+ wire [NUM_STREAMS-1:0] skd_valid, skd_last;
+ reg mid_packet, r_mid_packet;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ wire awskd_valid, wskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER), .DW(1))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(1'b0),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+ assign S_AXI_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ wire arskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(1))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data(1'b0),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXI_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+ assign S_AXI_RDATA = axil_read_data;
+ assign S_AXI_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // apply_wstrb(old_data, new_data, write_strobes)
+ // r_index, (wskd_index)
+ // {{{
+ assign wskd_index = apply_wstrb(
+ { {(C_AXI_DATA_WIDTH-LGNS){1'b0}}, r_index },
+ wskd_data, wskd_strb);
+
+ // r_index
+ initial r_index = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ r_index <= 0;
+ else if (axil_write_ready)
+ r_index <= wskd_index[LGNS-1:0];
+ // }}}
+
+ // axil_read_data
+ // {{{
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ axil_read_data <= 0;
+ axil_read_data[LGNS-1:0] <= r_index;
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+ // }}}
+
+ // function apply_wstrb
+ // {{{
+ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXI_DATA_WIDTH-1:0] prior_data;
+ input [C_AXI_DATA_WIDTH-1:0] new_data;
+ input [C_AXI_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The actual AXI switch
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Place a skidbuffer on every incoming stream input
+ // {{{
+ generate for(gk=0; gk<NUM_STREAMS; gk=gk+1)
+ begin
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXIS_DATA_WIDTH+1)
+ // }}}
+ ) skdswitch(//
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIS_TVALID[gk]),.o_ready(S_AXIS_TREADY[gk]),
+ .i_data({ S_AXIS_TDATA[gk*C_AXIS_DATA_WIDTH
+ +: C_AXIS_DATA_WIDTH], S_AXIS_TLAST[gk] }),
+ .o_valid(skd_valid[gk]), .i_ready(skd_switch_ready[gk]),
+ .o_data({ skd_data[gk], skd_last[gk] })
+ // }}}
+ );
+
+
+ end endgenerate
+ // }}}
+
+ // skd_switch_ready
+ // {{{
+ always @(*)
+ begin
+ skd_switch_ready = (1<<switch_index);
+ if (M_AXIS_TVALID && !M_AXIS_TREADY)
+ skd_switch_ready = 0;
+ if (!mid_packet && r_index != switch_index)
+ skd_switch_ready = 0;
+ end
+ // }}}
+
+ // mid_packet -- are we currently within a packet or not
+ // {{{
+ initial r_mid_packet = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_mid_packet <= 0;
+ else if (M_AXIS_TVALID)
+ r_mid_packet <= !M_AXIS_TLAST;
+
+ always @(*)
+ if (M_AXIS_TVALID)
+ mid_packet = !M_AXIS_TLAST;
+ else
+ mid_packet = r_mid_packet;
+ // }}}
+
+ // switch_index -- the current index of the skidbuffer switch
+ // {{{
+ initial switch_index = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ switch_index <= 0;
+ else if (!mid_packet)
+ switch_index <= r_index;
+ // }}}
+
+ // M_AXIS_TVALID
+ // {{{
+ initial M_AXIS_TVALID = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXIS_TVALID <= 1'b0;
+ else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ M_AXIS_TVALID <= |(skd_valid & skd_switch_ready);
+ // }}}
+
+ // M_AXIS_TDATA, M_AXIS_TLAST
+ // {{{
+ initial M_AXIS_TDATA = 0;
+ initial M_AXIS_TLAST = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ begin
+ M_AXIS_TDATA <= 0;
+ M_AXIS_TLAST <= 0;
+ end else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ begin
+ M_AXIS_TDATA <= skd_data[switch_index];
+ M_AXIS_TLAST <= skd_last[switch_index];
+
+ if (OPT_LOWPOWER && (skd_valid | skd_switch_ready) == 0)
+ begin
+ M_AXIS_TDATA <= 0;
+ M_AXIS_TLAST <= 0;
+ end
+ end
+ // }}}
+
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_ARADDR[ADDRLSB-1:0], awskd_addr, arskd_addr,
+ S_AXI_AWADDR[ADDRLSB-1:0], wskd_index };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used in verfiying this core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .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)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end
+
+ always @(*)
+ assert(S_AXI_RDATA[C_AXI_DATA_WIDTH-1:LGNS] == 0);
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN
+ && axil_read_ready))
+ begin
+ assert(S_AXI_RVALID);
+ assert(S_AXI_RDATA[LGNS-1:0] == $past(r_index));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXI_RVALID)
+ assert(S_AXI_RDATA == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Stream properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate for(gk=0; gk<NUM_STREAMS; gk=gk+1)
+ begin : S_STREAM_ASSUMPTIONS
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ assume(S_AXIS_TVALID[gk] == 0);
+ else if ($past(S_AXIS_TVALID[gk] && !S_AXIS_TREADY[gk]))
+ begin
+ assume(S_AXIS_TVALID[gk]);
+ assume($stable(S_AXIS_TDATA[gk*C_AXIS_DATA_WIDTH +: C_AXIS_DATA_WIDTH]));
+ assume($stable(S_AXIS_TLAST[gk]));
+ end
+ end endgenerate
+
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ assert(!M_AXIS_TVALID);
+ else if ($past(M_AXIS_TVALID && !M_AXIS_TREADY))
+ begin
+ assert(M_AXIS_TVALID);
+ assert($stable(M_AXIS_TDATA));
+ assert($stable(M_AXIS_TLAST));
+ end
+
+ always @(*)
+ if (OPT_LOWPOWER && !M_AXIS_TVALID)
+ begin
+ assert(M_AXIS_TDATA == 0);
+ assert(M_AXIS_TLAST == 0);
+ end
+
+ (* anyconst *) reg [LGNS-1:0] f_const_index;
+ (* anyconst *) reg [C_AXIS_DATA_WIDTH-1:0] f_never_data;
+ reg [LGNS-1:0] f_this_index;
+ reg [3:0] f_count, f_recount;
+ reg f_accepts, f_delivers;
+
+ always @(*)
+ assume(f_const_index < NUM_STREAMS);
+
+ // f_this_index
+ // {{{
+ initial f_this_index = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_this_index <= 1'b0;
+ else if (!M_AXIS_TVALID || M_AXIS_TREADY)
+ f_this_index <= switch_index;
+
+ always @(*)
+ assert(f_this_index < NUM_STREAMS);
+
+ always @(*)
+ assert(switch_index < NUM_STREAMS);
+ // }}}
+
+ // f_count
+ // {{{
+ always @(*)
+ begin
+ f_accepts = S_AXIS_TVALID[f_const_index]
+ && S_AXIS_TREADY[f_const_index];
+ f_delivers = M_AXIS_TVALID && M_AXIS_TREADY
+ && f_this_index == f_const_index;
+ end
+
+ initial f_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_count <= 0;
+ else case( { f_accepts, f_delivers })
+ 2'b01: f_count <= f_count - 1;
+ 2'b10: f_count <= f_count + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // f_count induction
+ // {{{
+ always @(*)
+ begin
+ f_recount = 0;
+ if (!S_AXIS_TREADY[f_const_index])
+ f_recount = 1;
+ if (M_AXIS_TVALID && f_this_index == f_const_index)
+ f_recount = f_recount + 1;
+
+ assert(f_recount == f_count);
+ end
+ // }}}
+
+ // f_this_index induction
+ always @(*)
+ if (M_AXIS_TVALID && !M_AXIS_TLAST)
+ assert(f_this_index == switch_index);
+
+ // assume != f_never_data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXIS_TVALID[f_const_index])
+ assume({ S_AXIS_TDATA[f_const_index * C_AXIS_DATA_WIDTH +: C_AXIS_DATA_WIDTH], S_AXIS_TLAST[f_const_index] } != f_never_data);
+ // }}}
+
+ // Never data induction
+ // {{{
+ always @(*)
+ begin
+ if (skd_valid[f_const_index])
+ assert({ skd_data[f_const_index], skd_last[f_const_index] } != f_never_data);
+ if (M_AXIS_TVALID && f_this_index == f_const_index)
+ assert({ M_AXIS_TDATA, M_AXIS_TLAST } != f_never_data);
+ end
+ // }}}
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // While there are already cover properties in the formal property
+ // set above, you'll probably still want to cover something
+ // application specific here
+
+ // }}}
+`endif
+ // }}}
+endmodule
diff --git a/rtl/wb2axip/axivcamera.v b/rtl/wb2axip/axivcamera.v
new file mode 100644
index 0000000..d5de9ad
--- /dev/null
+++ b/rtl/wb2axip/axivcamera.v
@@ -0,0 +1,1219 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axivcamera
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Reads a video frame from a source and writes the result to
+// memory.
+//
+// Registers:
+// {{{
+// 0: FBUF_CONTROL and status
+// bit 0: START(1)/STOP(0)
+// Command the core to start by writing a '1' to this bit. It
+// will then remain busy/active until either you tell it to halt,
+// or an error occurrs.
+// bit 1: BUSY
+// bit 2: ERR
+// If the core receives a bus error, it assumes it has been
+// inappropriately set up, sets this error bit and then halts.
+// It will not start again until this bit is cleared. Only a
+// write to the control register with the ERR bit set will clear
+// it. (Don't do this unless you know what caused it to halt ...)
+// bit 3: DIRTY
+// If you update core parameters while it is running, the busy
+// bit will be set. This bit is an indication that the current
+// configuration doesn't necessarily match the one you are reading
+// out. To clear DIRTY, deactivate the core, wait for it to be
+// no longer busy, and then start it again. This will also start
+// it under the new configuration so the two match.
+// bits 15-8: FRAMES
+// Indicates the number of frames you want to copy. Set this to
+// 0 to continuously copy, or a finite number to grab only that
+// many frames.
+//
+// As a feature, this isn't perhaps the most useful, since every
+// frame will be written to the same location. A more useful
+// approach would be to increase the desired number of lines to
+// (NLINES * NFRAMES), and then to either leave this number at zero
+// or set it to one.
+//
+// 2: FBUF_LINESTEP
+// Controls the distance from one line to the next. This is the
+// value added to the address of the beginning of the line to get
+// to the beginning of the next line. This should nominally be
+// equal to the number of bytes per line, although it doesn't
+// need to be.
+//
+// Any attempt to set this value to zero will simply copy the
+// number of data bytes per line (rounded down to the neareset
+// word) into this value. This is a recommended approach to
+// setting this value.
+//
+// 4: FBUF_LINEBYTES
+// This is the number of data bytes necessary to capture all of
+// the video data in a line. This value must be more than zero
+// in order to activate the core.
+//
+// At present, this core can only handle a number of bytes aligned
+// with the word size of the bus. To convert from bytes to words,
+// round any fractional part upwards (i.e. ceil()). The core
+// will naturally round down internally.
+//
+// 6: FBUF_NUMLINES
+// The number of lines of active video data in a frame. This
+// number must be greater than zero in order to activate and
+// operate the core.
+//
+// 8: FBUF_ADDRESS
+// The is the first address of video data in memory. Each frame
+// will start reading from this address.
+//
+// 12: (reserved for the upper FBUF_ADDRESS)
+//
+// KNOWN ISSUES:
+//
+// Does not support interlacing (yet). (Interlacing is not on my "todo"
+// list, so it might take a while to get said support if you need it.)
+//
+// Does not support unaligned addressing. The frame buffer address, and
+// the line step, must all be word aligned. While nothing is stopping
+// me from supporting unaligned addressing, it was such a pain to do the
+// last time that I might just hold off until I have a paying customer
+// who wants it.
+//
+// Line bytes that include a fraction of a word are rounded down and not
+// up.
+// }}}
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axivcamera #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 1,
+ //
+ // We support five 32-bit AXI-lite registers, requiring 5-bits
+ // of AXI-lite addressing
+ localparam C_AXIL_ADDR_WIDTH = 4,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ //
+ // The bottom ADDRLSB bits of any AXI address are subword bits
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3,
+ //
+ // OPT_LGMAXBURST
+ parameter OPT_LGMAXBURST = 8,
+ //
+ parameter [0:0] DEF_ACTIVE_ON_RESET = 0,
+ parameter [15:0] DEF_LINES_PER_FRAME = 1024,
+ parameter [16-ADDRLSB-1:0] DEF_WORDS_PER_LINE = (1280 * 32)/C_AXI_DATA_WIDTH,
+ //
+ // DEF_FRAMEADDR: the default AXI address of the frame buffer
+ // containing video memory. Unless OPT_UNALIGNED is set, this
+ // should be aligned so that DEF_FRAMEADDR[ADDRLSB-1:0] == 0.
+ parameter [C_AXI_ADDR_WIDTH-1:0] DEF_FRAMEADDR = 0,
+ //
+ // The (log-based two of the) size of the FIFO in words.
+ // I like to set this to the size of two AXI bursts, so that
+ // while one is being read out the second can be read in. Can
+ // also be set larger if desired.
+ parameter OPT_LGFIFO = OPT_LGMAXBURST+1,
+ localparam LGFIFO = (OPT_LGFIFO < OPT_LGMAXBURST+1)
+ ? OPT_LGMAXBURST+1 : OPT_LGFIFO,
+ //
+ // AXI_ID is the ID we will use for all of our AXI transactions
+ parameter AXI_ID = 0,
+ //
+ // OPT_IGNORE_HLAST
+ parameter [0:0] OPT_IGNORE_HLAST = 0,
+ //
+ // OPT_TUSER_IS_SOF. Xilinx and I chose different encodings.
+ // I encode TLAST == VLAST, and TUSER == HLAST (optional).
+ // Xilinx likes TLAST == HLAST and TUSER == SOF (start of frame)
+ // Set OPT_TUSER_IS_SOF to use Xilinx's encoding
+ parameter [0:0] OPT_TUSER_IS_SOF = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The incoming video stream data/pixel interface
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire /* VLAST */ S_AXIS_TLAST,
+ input wire /* HLAST */ S_AXIS_TUSER,
+ // }}}
+ //
+ // The control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ //
+
+ //
+ // The AXI (full) write-only interface
+ // {{{
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [7:0] M_AXI_AWLEN,
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP
+ // }}}
+ // }}}
+ );
+
+ // Core logic implementation
+ // {{{
+ // Local parameter declarations
+ // {{{
+ localparam [1:0] FBUF_CONTROL = 2'b00,
+ FBUF_FRAMEINFO = 2'b01,
+ FBUF_ADDRLO = 2'b10,
+ FBUF_ADDRHI = 2'b11;
+
+ localparam CBIT_ACTIVE = 0,
+ CBIT_BUSY = 1,
+ CBIT_ERR = 2,
+ // CBIT_DIRTY = 3,
+ CBIT_LOST_SYNC = 4;
+
+ localparam TMPLGMAXBURST=(LGFIFO-1 > OPT_LGMAXBURST)
+ ? OPT_LGMAXBURST : LGFIFO-1;
+
+ localparam LGMAXBURST = (TMPLGMAXBURST+ADDRLSB > 12)
+ ? (12-ADDRLSB) : TMPLGMAXBURST;
+ // }}}
+
+ wire i_clk = S_AXI_ACLK;
+ wire i_reset = !S_AXI_ARESETN;
+
+ // Signal declarations
+ // {{{
+ reg soft_reset, r_err, r_stopped, lost_sync;
+ reg cfg_active, cfg_zero_length,
+ cfg_continuous;
+ reg [C_AXI_ADDR_WIDTH-1:0] cfg_frame_addr;
+ reg [15:0] cfg_frame_lines, cfg_line_step;
+ reg [16-ADDRLSB-1:0] cfg_line_words;
+
+ // FIFO signals
+ wire reset_fifo, write_to_fifo,
+ read_from_fifo;
+ wire [C_AXI_DATA_WIDTH-1:0] write_data, fifo_data;
+ wire [LGFIFO:0] fifo_fill;
+ wire fifo_full, fifo_empty,
+ fifo_vlast, fifo_hlast;
+
+ wire awskd_valid, axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ //
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire arskd_valid, axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+ reg [C_AXIL_DATA_WIDTH-1:0] w_status_word;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, new_wideaddr;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi;
+ reg [C_AXIL_DATA_WIDTH-1:0] wide_config;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_config;
+
+ reg axi_awvalid, axi_wvalid, axi_wlast,
+ phantom_start, start_burst,
+ aw_none_outstanding;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [7:0] axi_awlen, next_awlen;
+ reg [8:0] wr_pending;
+ reg [15:0] aw_bursts_outstanding;
+ //
+ // reg vlast;
+ // reg [15:0] r_frame_lines, r_line_step;
+ reg [16-ADDRLSB-1:0] next_line_words;
+
+ reg req_hlast, req_vlast, req_newline;
+ reg [15:0] req_nlines;
+ reg [7:0] req_nframes;
+ reg [16-ADDRLSB-1:0] req_line_words;
+ reg [C_AXI_ADDR_WIDTH:0] req_addr, req_line_addr, next_line_addr;
+ //
+ reg wr_hlast, wr_vlast;
+ reg [15:0] wr_lines;
+ reg [16-ADDRLSB-1:0] wr_line_beats;
+ // wire no_fifo__available;
+ //
+ reg [LGMAXBURST-1:0] till_boundary;
+ reg [LGFIFO:0] fifo_data_available;
+ reg fifo_bursts_available;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // This is mostly the skidbuffer logic, and handling of the VALID
+ // and READY signals for the AXI-lite control logic in the next
+ // section.
+ // {{{
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite controlled logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // soft_reset, r_err
+ // {{{
+ initial soft_reset = 1;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ soft_reset <= 1;
+ r_err <= 0;
+ end else if (r_stopped)
+ begin
+
+ if (axil_write_ready && awskd_addr == FBUF_CONTROL
+ && wskd_strb[0] && wskd_data[CBIT_ERR])
+ begin
+ r_err <= cfg_zero_length;
+ soft_reset <= 0;
+ end
+
+ if (!r_err)
+ soft_reset <= 0;
+
+ end else // if (!soft_reset)
+ begin
+ // Halt on any bus error. We'll require user intervention
+ // to start back up again
+ if ((M_AXI_BVALID && M_AXI_BREADY && M_AXI_BRESP[1])
+ ||(req_addr[C_AXI_ADDR_WIDTH]))
+ begin
+ soft_reset <= 1;
+ r_err <= 1;
+ end
+
+ // Halt on any user request
+ if (!cfg_active)
+ soft_reset <= 1;
+ end
+ // }}}
+
+ // wide_* and new_* write setup registers
+ // {{{
+ always @(*)
+ begin
+ wide_address = 0;
+ wide_address[C_AXI_ADDR_WIDTH-1:0] = cfg_frame_addr;
+ wide_address[ADDRLSB-1:0] = 0;
+
+ wide_config = { cfg_frame_lines, cfg_line_words,
+ {(ADDRLSB){1'b0}} };
+ end
+
+ assign new_cmdaddrlo = apply_wstrb(
+ wide_address[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+
+ generate if (C_AXI_ADDR_WIDTH > 32)
+ begin : GEN_LARGE_AW
+
+ assign new_cmdaddrhi = apply_wstrb(
+ wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+
+ end else begin : SINGLE_WORD_AW
+
+ assign new_cmdaddrhi = 0;
+
+ end endgenerate
+
+
+ wire [C_AXIL_DATA_WIDTH-1:0] new_control;
+ assign new_control = apply_wstrb(w_status_word, wskd_data, wskd_strb);
+ assign new_config = apply_wstrb(wide_config, wskd_data, wskd_strb);
+
+ always @(*)
+ begin
+ new_wideaddr = wide_address;
+
+ if (awskd_addr == FBUF_ADDRLO)
+ new_wideaddr[C_AXIL_DATA_WIDTH-1:0] = new_cmdaddrlo;
+ if (awskd_addr == FBUF_ADDRHI)
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH] = new_cmdaddrhi;
+ new_wideaddr[ADDRLSB-1:0] = 0;
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+ end
+ // }}}
+
+ // Configuration registers (Write)
+ // {{{
+ initial cfg_active = 0;
+ initial cfg_continuous = 0;
+ initial cfg_frame_addr = DEF_FRAMEADDR;
+ initial cfg_frame_addr[ADDRLSB-1:0] = 0;
+ initial cfg_line_words = DEF_WORDS_PER_LINE;
+ initial cfg_frame_lines = DEF_LINES_PER_FRAME;
+ initial cfg_zero_length = (DEF_WORDS_PER_LINE == 0)
+ ||(DEF_LINES_PER_FRAME == 0);
+ initial cfg_line_step = { DEF_WORDS_PER_LINE, {(ADDRLSB){1'b0}} };
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ cfg_active <= DEF_ACTIVE_ON_RESET;
+ cfg_frame_addr <= DEF_FRAMEADDR;
+ cfg_line_words <= DEF_WORDS_PER_LINE;
+ cfg_line_step <= { DEF_WORDS_PER_LINE, {(ADDRLSB){1'b0}} };
+ cfg_frame_lines <= DEF_LINES_PER_FRAME;
+ cfg_zero_length <= (DEF_WORDS_PER_LINE==0)
+ ||(DEF_LINES_PER_FRAME == 0);
+ req_nframes <= 0;
+ cfg_continuous <= 0;
+ end else begin
+ if (phantom_start && req_vlast && req_hlast && req_nframes > 0)
+ req_nframes <= req_nframes - 1;
+
+ if (axil_write_ready)
+ case(awskd_addr)
+ FBUF_CONTROL: begin
+ if (wskd_strb[0])
+ cfg_active <= (cfg_active || r_stopped)
+ && wskd_data[CBIT_ACTIVE]
+ && (!r_err || wskd_data[CBIT_ERR])
+ && (!cfg_zero_length);
+
+ if (!cfg_active && r_stopped && wskd_strb[1])
+ begin
+ req_nframes <= wskd_data[15:8];
+ cfg_continuous <= (wskd_data[15:8] == 0);
+ end
+
+ if (!cfg_active && r_stopped)
+ begin
+ if (new_control[31:16] == 0)
+ begin
+ cfg_line_step <= 0;
+ cfg_line_step[16-1:ADDRLSB] <= cfg_line_words;
+ end else
+ cfg_line_step <= new_control[31:16];
+ end end
+ FBUF_FRAMEINFO:
+ if (!cfg_active && r_stopped)
+ begin
+ { cfg_frame_lines, cfg_line_words }
+ <= new_config[C_AXIL_DATA_WIDTH-1:ADDRLSB];
+ cfg_zero_length <= (new_config[31:16] == 0)
+ ||(new_config[15:ADDRLSB] == 0);
+ end
+ FBUF_ADDRLO, FBUF_ADDRHI: if (!cfg_active && r_stopped)
+ cfg_frame_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ default: begin end
+ endcase
+
+ if (M_AXI_BREADY && M_AXI_BVALID && M_AXI_BRESP[1])
+ cfg_active <= 0;
+ if (req_addr[C_AXI_ADDR_WIDTH])
+ cfg_active <= 0;
+ if (phantom_start && req_vlast && req_hlast && req_nframes <= 1
+ && !cfg_continuous)
+ cfg_active <= 0;
+
+ cfg_line_step[ADDRLSB-1:0] <= 0;
+ cfg_frame_addr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // AXI-Lite read register data
+ // {{{
+ always @(*)
+ begin
+ w_status_word = 0;
+ w_status_word[31:16] = cfg_line_step;
+ w_status_word[15:8] = req_nframes;
+ w_status_word[CBIT_LOST_SYNC] = lost_sync;
+ // w_status_word[CBIT_DIRTY] = cfg_dirty;
+ w_status_word[CBIT_ERR] = r_err;
+ w_status_word[CBIT_BUSY] = !soft_reset;
+ w_status_word[CBIT_ACTIVE] = cfg_active || (!soft_reset || !r_stopped);
+ end
+
+ always @(posedge i_clk)
+ if (!axil_read_valid || S_AXIL_RREADY)
+ begin
+ axil_read_data <= 0;
+ case(arskd_addr)
+ FBUF_CONTROL: axil_read_data <= w_status_word;
+ FBUF_FRAMEINFO: axil_read_data <= { cfg_frame_lines,
+ cfg_line_words, {(ADDRLSB){1'b0}} };
+ FBUF_ADDRLO: axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0];
+ FBUF_ADDRHI: axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ default axil_read_data <= 0;
+ endcase
+ end
+ // }}}
+
+ // apply_wstrb function for applying wstrbs to register words
+ // {{{
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The data FIFO section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ wire S_AXIS_SOF, S_AXIS_HLAST, S_AXIS_VLAST;
+ generate if (OPT_TUSER_IS_SOF)
+ begin : XILINX_SOF_LOCK
+ reg [15:0] axis_line;
+ reg axis_last_line;
+
+ assign S_AXIS_HLAST = S_AXIS_TLAST;
+ assign S_AXIS_SOF = S_AXIS_TUSER;
+
+ // Generate S_AXIS_VLAST from S_AXIS_SOF
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axis_line <= 0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ begin
+ if (S_AXIS_SOF)
+ axis_line <= 0;
+ else if (S_AXIS_HLAST)
+ axis_line <= axis_line + 1;
+ end
+
+ always @(posedge S_AXI_ACLK)
+ axis_last_line <= (axis_line+1 >= cfg_frame_lines);
+
+ assign S_AXIS_VLAST = axis_last_line && S_AXIS_HLAST;
+
+ end else begin : VLAST_LOCK
+
+ assign S_AXIS_VLAST = S_AXIS_TLAST;
+
+ assign S_AXIS_HLAST = S_AXIS_TUSER;
+
+ /*
+ initial S_AXIS_SOF = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXIS_SOF <= 1'b0;
+ else if (S_AXIS_TVALID && S_AXIS_TREADY)
+ S_AXIS_SOF <= S_AXIS_VLAST;
+ */
+ assign S_AXIS_SOF = 1'b0;
+
+ // Verilator lint_off UNUSED
+ wire unused_sof;
+ assign unused_sof = &{ 1'b0, S_AXIS_SOF };
+ // Verilator lint_on UNUSED
+ end endgenerate
+
+ // Read/write signaling, lost_sync detection
+ // {{{
+ assign reset_fifo = (!cfg_active || r_stopped || lost_sync);
+
+ assign write_to_fifo = S_AXIS_TVALID && !lost_sync && !fifo_full;
+ assign write_data = S_AXIS_TDATA;
+ assign read_from_fifo = (M_AXI_WVALID && M_AXI_WREADY);
+ assign S_AXIS_TREADY = 1'b1;
+
+ initial lost_sync = 1;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!S_AXI_ARESETN || !cfg_active || r_stopped)
+ lost_sync <= 0;
+ else if (M_AXI_WVALID && M_AXI_WREADY
+ &&((!OPT_IGNORE_HLAST&&(wr_hlast != fifo_hlast))
+ || (!wr_hlast && fifo_vlast)
+ || (wr_vlast && wr_hlast && !fifo_vlast)))
+ // Here is where we might possibly notice we've lost sync
+ lost_sync <= 1;
+
+ // lost_sync <= 1'b0;
+ end
+ // }}}
+
+ generate if (LGFIFO > 0)
+ begin : GEN_SPACE_AVAILBLE
+ // Here's where we'll put the actual outgoing FIFO
+ // {{{
+ reg [LGFIFO:0] next_data_available;
+ always @(*)
+ begin
+ next_data_available = fifo_data_available;
+ // Verilator lint_off WIDTH
+ if (phantom_start)
+ next_data_available = fifo_data_available - (M_AXI_AWLEN + 1);
+ // Verilator lint_on WIDTH
+ if (write_to_fifo)
+ next_data_available = next_data_available + 1;
+ end
+
+ initial fifo_data_available = 0;
+ initial fifo_bursts_available = 0;
+ always @(posedge i_clk)
+ if (reset_fifo)
+ begin
+ fifo_data_available <= 0;
+ fifo_bursts_available <= 0;
+ end else if (phantom_start || write_to_fifo)
+ begin
+ fifo_data_available <= next_data_available;
+ fifo_bursts_available <= |next_data_available[LGFIFO:LGMAXBURST];
+ end
+
+ sfifo #(.BW(C_AXI_DATA_WIDTH+2), .LGFLEN(LGFIFO))
+ sfifo(i_clk, reset_fifo,
+ write_to_fifo, { S_AXIS_VLAST,
+ (!OPT_IGNORE_HLAST && S_AXIS_HLAST),write_data},
+ fifo_full, fifo_fill,
+ read_from_fifo, { fifo_vlast, fifo_hlast,
+ fifo_data }, fifo_empty);
+ // }}}
+ end else begin : NO_FIFO
+ // {{{
+ //
+ // This isn't verified or tested. I'm not sure I'd expect this
+ // to work at all.
+ assign fifo_full = M_AXI_WVALID && !M_AXI_WREADY;
+ assign fifo_fill = 0;
+ assign fifo_empty = !S_AXIS_TVALID;
+ assign fifo_vlast = S_AXIS_VLAST;
+ assign fifo_hlast = (!OPT_IGNORE_HLAST && S_AXIS_HLAST);
+
+ assign fifo_data = write_data;
+ // }}}
+ end endgenerate
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outoing frame address counting
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg w_frame_needs_alignment, frame_needs_alignment,
+ line_needs_alignment,
+ line_multiple_bursts, req_needs_alignment, req_multiple_bursts;
+
+ // frame_needs_alignment
+ // {{{
+ always @(*)
+ begin
+ w_frame_needs_alignment = 0;
+ if (|cfg_line_words[15-ADDRLSB:LGMAXBURST])
+ w_frame_needs_alignment = 1;
+ if (~cfg_frame_addr[ADDRLSB +: LGMAXBURST]
+ < cfg_line_words[LGMAXBURST-1:0])
+ w_frame_needs_alignment = 1;
+ if (cfg_frame_addr[ADDRLSB +: LGMAXBURST] == 0)
+ w_frame_needs_alignment = 0;
+ end
+
+ always @(posedge i_clk)
+ if (!cfg_active && r_stopped)
+ frame_needs_alignment <= w_frame_needs_alignment;
+ // }}}
+
+ // line_needs_alignment
+ // {{{
+ always @(posedge i_clk)
+ if (r_stopped)
+ line_multiple_bursts <= (cfg_line_words >= (1<<LGMAXBURST));
+
+ always @(*)
+ if (!cfg_active || req_vlast)
+ next_line_addr = { 1'b0, cfg_frame_addr };
+ else
+ next_line_addr = req_line_addr+ { {(C_AXI_ADDR_WIDTH-16){1'b0}}, cfg_line_step };
+
+ always @(posedge i_clk)
+ if (req_newline)
+ begin
+ line_needs_alignment <= 0;
+ if (|next_line_addr[ADDRLSB +: LGMAXBURST])
+ begin
+ if (|cfg_line_words[15-ADDRLSB:LGMAXBURST])
+ line_needs_alignment <= 1;
+ if (~next_line_addr[ADDRLSB +: LGMAXBURST]
+ < cfg_line_words[LGMAXBURST-1:0])
+ line_needs_alignment <= 1;
+ end
+ end
+ // }}}
+
+ // req_addr, req_line_addr, req_line_words
+ // {{{
+ always @(*)
+ // Verilator lint_off WIDTH
+ next_line_words = req_line_words - (M_AXI_AWLEN+1);
+ // Verilator lint_on WIDTH
+
+ initial req_addr = 0;
+ initial req_line_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ req_addr <= { 1'b0, cfg_frame_addr };
+ req_line_addr <= { 1'b0, cfg_frame_addr };
+ req_line_words <= cfg_line_words;
+ req_newline <= 1;
+ req_multiple_bursts <= (cfg_line_words >= (1<<LGMAXBURST));
+ req_needs_alignment <= w_frame_needs_alignment;
+ end else if (phantom_start)
+ begin
+ req_newline <= req_hlast;
+ req_needs_alignment <= 0;
+ if (req_hlast && req_vlast)
+ begin
+ req_addr <= { 1'b0, cfg_frame_addr };
+ req_line_addr <= { 1'b0, cfg_frame_addr };
+ req_line_words <= cfg_line_words;
+ req_multiple_bursts <= line_multiple_bursts;
+
+ req_needs_alignment <= frame_needs_alignment;
+ end else if (req_hlast)
+ begin
+ // verilator lint_off WIDTH
+ req_addr <= req_line_addr + cfg_line_step;
+ req_line_addr <= req_line_addr + cfg_line_step;
+ // verilator lint_on WIDTH
+ req_line_words <= cfg_line_words;
+ req_needs_alignment <= line_needs_alignment;
+ req_multiple_bursts <= line_multiple_bursts;
+ end else begin
+ req_addr <= req_addr + (1<<(LGMAXBURST+ADDRLSB));
+ req_line_words <= next_line_words;
+ req_multiple_bursts <= |next_line_words[15-ADDRLSB:LGMAXBURST];
+
+ req_addr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ end else
+ req_newline <= 0;
+ // }}}
+
+ // req_nlines, req_vlast
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ req_nlines <= cfg_frame_lines-1;
+ req_vlast <= (cfg_frame_lines <= 1);
+ end else if (phantom_start && req_hlast)
+ begin
+ if (req_vlast)
+ begin
+ req_nlines <= cfg_frame_lines-1;
+ req_vlast <= (cfg_frame_lines <= 1);
+ end else begin
+ req_nlines <= req_nlines - 1;
+ req_vlast <= (req_nlines <= 1);
+ end
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing sync counting
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // wr_line_beats, wr_hlast
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ wr_line_beats <= cfg_line_words-1;
+ wr_hlast <= (cfg_line_words == 1);
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ if (wr_hlast)
+ begin
+ wr_line_beats <= cfg_line_words - 1;
+ wr_hlast <= (cfg_line_words <= 1);
+ end else begin
+ wr_line_beats <= wr_line_beats - 1;
+ wr_hlast <= (wr_line_beats <= 1);
+ end
+ end
+ // }}}
+
+ // wr_lines, wr_vlast
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ wr_lines <= cfg_frame_lines-1;
+ wr_vlast <= (cfg_frame_lines == 1);
+ end else if (M_AXI_WVALID && M_AXI_WREADY && wr_hlast)
+ begin
+ if (wr_vlast)
+ begin
+ wr_lines <= cfg_frame_lines - 1;
+ wr_vlast <= (cfg_frame_lines <= 1);
+ end else begin
+ wr_lines <= wr_lines - 1;
+ wr_vlast <= (wr_lines <= 1);
+ end
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The outgoing AXI (full) protocol section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Some counters to keep track of our state
+ // {{{
+
+ // aw_bursts_outstanding
+ // {{{
+ // Count the number of bursts outstanding--these are the number of
+ // AWVALIDs that have been accepted, but for which the BVALID
+ // has not (yet) been returned.
+ initial aw_none_outstanding = 1;
+ initial aw_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ aw_bursts_outstanding <= 0;
+ aw_none_outstanding <= 1;
+ end else case ({ phantom_start, M_AXI_BVALID && M_AXI_BREADY })
+ 2'b01: begin
+ aw_bursts_outstanding <= aw_bursts_outstanding - 1;
+ aw_none_outstanding <= (aw_bursts_outstanding == 1);
+ end
+ 2'b10: begin
+ aw_bursts_outstanding <= aw_bursts_outstanding + 1;
+ aw_none_outstanding <= 0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // r_stopped
+ // {{{
+ // Following an error or drop of cfg_active, soft_reset will go high.
+ // We then come to a stop once everything becomes inactive
+ initial r_stopped = 1;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_stopped <= 1;
+ else if (r_stopped)
+ begin
+ // Synchronize on the last pixel of an incoming frame
+ if (cfg_active && S_AXIS_TVALID && S_AXIS_VLAST)
+ r_stopped <= soft_reset;
+ end else if ((soft_reset || lost_sync) && aw_none_outstanding
+ && !M_AXI_AWVALID && !M_AXI_WVALID)
+ r_stopped <= 1;
+ // }}}
+
+ // }}}
+
+ //
+ // start_burst
+ // {{{
+ always @(*)
+ begin
+ start_burst = 1;
+ if (LGFIFO > 0 && !fifo_bursts_available)
+ begin
+ if (!req_multiple_bursts && fifo_data_available[7:0]
+ < req_line_words[7:0])
+ start_burst = 0;
+ if (req_multiple_bursts && !fifo_bursts_available)
+ start_burst = 0;
+ end
+
+ if (phantom_start || req_newline || lost_sync)
+ start_burst = 0;
+
+ // Can't start a new burst if the outgoing channel is still
+ // stalled.
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ start_burst = 0;
+ if (M_AXI_WVALID && (!M_AXI_WREADY || !M_AXI_WLAST))
+ start_burst = 0;
+
+ // Don't let our aw_bursts_outstanding counter overflow
+ if (aw_bursts_outstanding[15])
+ start_burst = 0;
+
+ // Don't wrap around memory
+ if (req_addr[C_AXI_ADDR_WIDTH])
+ start_burst = 0;
+
+ // If the user wants us to stop, then stop
+ if (soft_reset || !cfg_active || r_stopped)
+ start_burst = 0;
+ end
+ // }}}
+
+ // AWLEN
+ // {{{
+ always @(*)
+ till_boundary = ~req_addr[ADDRLSB +: LGMAXBURST];
+
+ always @(*)
+ begin
+ if (req_needs_alignment)
+ next_awlen = till_boundary;
+ else if (req_multiple_bursts)
+ next_awlen = (1<<LGMAXBURST)-1;
+ else
+ next_awlen = req_line_words[7:0]-1;
+ end
+
+ always @(posedge i_clk)
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awlen <= next_awlen;
+ // }}}
+
+ // wr_pending
+ // {{{
+ initial wr_pending = 0;
+ always @(posedge i_clk)
+ begin
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ wr_pending <= wr_pending - 1;
+ if (start_burst)
+ wr_pending <= next_awlen + 1;
+
+ if (!S_AXI_ARESETN)
+ wr_pending <= 0;
+ end
+ // }}}
+
+ // req_hlast
+ // {{{
+ always @(posedge i_clk)
+ // Verilator lint_off WIDTH
+ if (r_stopped)
+ req_hlast <= (cfg_frame_addr[ADDRLSB +: LGMAXBURST]
+ + cfg_line_words <= (1<<LGMAXBURST));
+ else if (phantom_start)
+ begin
+ if (req_hlast && req_vlast)
+ req_hlast <= (cfg_frame_addr[ADDRLSB +: LGMAXBURST]
+ + cfg_line_words <= (1<<LGMAXBURST));
+ else if (req_hlast)
+ req_hlast <= (next_line_addr[ADDRLSB +: LGMAXBURST]
+ + cfg_line_words <= (1<<LGMAXBURST));
+ else
+ req_hlast <= (req_line_words - (M_AXI_AWLEN+1)
+ <= (1<<LGMAXBURST));
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // phantom_start
+ // {{{
+ initial phantom_start = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ phantom_start <= 0;
+ else
+ phantom_start <= start_burst;
+ // }}}
+
+ // AWVALID
+ // {{{
+ initial axi_awvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awvalid <= start_burst;
+ // }}}
+
+ // ARADDR
+ // {{{
+ initial axi_awaddr = 0;
+ always @(posedge i_clk)
+ begin
+ if (start_burst)
+ axi_awaddr <= req_addr[C_AXI_ADDR_WIDTH-1:0];
+
+ axi_awaddr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // WVALID
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_wvalid <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wvalid <= start_burst || (M_AXI_WVALID && !M_AXI_WLAST);
+ // }}}
+
+ // WLAST
+ // {{{
+ always @(posedge i_clk)
+ begin
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ axi_wlast <= (wr_pending <= 2);
+
+ if (start_burst)
+ axi_wlast <= (next_awlen == 0);
+ end
+ // }}}
+
+
+ // Set the constant M_AXI_* signals
+ // {{{
+ assign M_AXI_AWVALID= axi_awvalid;
+ assign M_AXI_AWID = AXI_ID;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = axi_awlen;
+ // Verilator lint_off WIDTH
+ assign M_AXI_AWSIZE = $clog2(C_AXI_DATA_WIDTH)-3;
+ // Verilator lint_on WIDTH
+ assign M_AXI_AWBURST= 2'b01; // INCR
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE= 0;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+ //
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WLAST = axi_wlast;
+ assign M_AXI_WDATA = fifo_data;
+ assign M_AXI_WSTRB = -1;
+ //
+ assign M_AXI_BREADY = 1;
+ // }}}
+
+ // End AXI protocol section
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_BID,
+ M_AXI_BRESP[0], fifo_empty,
+ S_AXIL_AWADDR[AXILLSB-1:0], S_AXIL_ARADDR[AXILLSB-1:0],
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_control, new_config, fifo_fill, next_line_addr,
+ fifo_vlast, fifo_hlast
+ };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+//
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // {{{
+
+ // The formal properties for this core are maintained elsewhere
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // The formal proof for this core doesn't (yet) include a contract
+ // check.
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Simplifying (careless) assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // If the FIFO gets reset, then we really don't care about what
+ // gets written to memory. However, it is possible that a value
+ // on the output might get changed and so violate our protocol checker.
+ // (We don't care.) So let's just assume it never happens, and check
+ // everything else instead.
+ always @(*)
+ if (M_AXI_WVALID)
+ assume(!lost_sync && cfg_active);
+ // }}}
+ // }}}
+`endif
+endmodule
diff --git a/rtl/wb2axip/axivdisplay.v b/rtl/wb2axip/axivdisplay.v
new file mode 100644
index 0000000..aac6c62
--- /dev/null
+++ b/rtl/wb2axip/axivdisplay.v
@@ -0,0 +1,1438 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axivdisplay
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Reads from memory for the purposes of serving a video frame
+// {{{
+// buffer. The result of this core is an AXI stream having the
+// word size of the memory interface in the first place. TLAST is set
+// based upon the end of the frame, and TUSER based upon the end of the
+// line. An option exists to use Xilinx's encoding instead where TLAST
+// is the end of the line and TUSER is the first pixel word of the frame.
+//
+// This core is in many ways similar to the AXI MM2S core, but with some
+// key differences: The AXI MM2S core generates a single transfer. This
+// core generates a repeating set of transfers--one per line, repeated
+// per frame.
+//
+// To insure high bandwidth, data is first copied into a FIFO of twice
+// the width of the longest burst.
+//
+// }}}
+// Registers:
+// {{{
+// 0: FBUF_CONTROL and status
+// bit 0: START(1)/STOP(0)
+// Command the core to start by writing a '1' to this bit. It
+// will then remain busy/active until either you tell it to halt,
+// or an error occurrs.
+// bit 1: BUSY
+// bit 2: ERR
+// If the core receives a bus error, it assumes it has been
+// inappropriately set up, sets this error bit and then halts.
+// It will not start again until this bit is cleared. Only a
+// write to the control register with the ERR bit set will clear
+// it. (Don't do this unless you know what caused it to halt ...)
+// bit 3: DIRTY
+// If you update core parameters while it is running, the busy
+// bit will be set. This bit is an indication that the current
+// configuration doesn't necessarily match the one you are reading
+// out. To clear DIRTY, deactivate the core, wait for it to be
+// no longer busy, and then start it again. This will also start
+// it under the new configuration so the two match.
+//
+// 2: FBUF_LINESTEP
+// Controls the distance from one line to the next. This is the
+// value added to the address of the beginning of the line to get
+// to the beginning of the next line. This should nominally be
+// equal to the number of bytes per line, although it doesn't
+// need to be.
+//
+// Any attempt to set this value to zero will simply copy the
+// number of data bytes per line into this value.
+//
+// 4: FBUF_LINEBYTES
+// This is the number of data bytes necessary to capture all of
+// the video data in a line. This value must be more than zero
+// in order to activate the core.
+//
+// At present, this core can only handle a number of bytes aligned
+// with the word size of the bus.
+//
+// 6: FBUF_NUMLINES
+// The number of lines of active video data in a frame. This
+// number must be greater than zero in order to activate and
+// operate the core.
+//
+// 8: FBUF_ADDRESS
+// The is the first address of video data in memory. Each frame
+// will start reading from this address.
+//
+// 12: (reserved for the upper FBUF_ADDRESS)
+//
+// BUGS:
+// 1. Memory reads are currently allowed to wrap around the end of memory.
+// I'm not (yet) sure if this is a good thing or a bad thing. I mean, its
+// a great thing for small dedicated memories designated for this purpose
+// alone, but perhaps a bad thing if the memory space is shared with
+// peripherals as well as a CPU.
+//
+// 2. I'd still like to add an option to handle memory addresses that
+// aren't aligned. Until that is done, the means of interacting with the
+// core will change from one implementation to another.
+//
+// 3. If the configuration changes mid-write, but without de-activating
+// the core and waiting for it to come to idle, the synchronization flags
+// might out-of-sync with the pixels. To resolve, deactivate the core and
+// let it come to whenever changing configuration parameters.
+// }}}
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axivdisplay #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 1,
+ //
+ // We support five 32-bit AXI-lite registers, requiring 5-bits
+ // of AXI-lite addressing
+ localparam C_AXIL_ADDR_WIDTH = 4,
+ localparam C_AXIL_DATA_WIDTH = 32,
+ //
+ // The bottom ADDRLSB bits of any AXI address are subword bits
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3,
+ localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3,
+ //
+ // OPT_UNALIGNED: Allow unaligned accesses, address requests
+ // and sizes which may or may not match the underlying data
+ // width. If set, the core will quietly align these requests.
+ // parameter [0:0] OPT_UNALIGNED = 1'b0,
+ //
+ // OPT_LGMAXBURST
+ parameter OPT_LGMAXBURST = 8,
+ //
+ parameter [0:0] DEF_ACTIVE_ON_RESET = 0,
+ parameter [15:0] DEF_LINES_PER_FRAME = 1024,
+ parameter [16-ADDRLSB-1:0] DEF_WORDS_PER_LINE = (1280 * 32)/C_AXI_DATA_WIDTH,
+ //
+ // DEF_FRAMEADDR: the default AXI address of the frame buffer
+ // containing video memory. Unless OPT_UNALIGNED is set, this
+ // should be aligned so that DEF_FRAMEADDR[ADDRLSB-1:0] == 0.
+ parameter [C_AXI_ADDR_WIDTH-1:0] DEF_FRAMEADDR = 0,
+ //
+ // The (log-based two of the) size of the FIFO in words.
+ // I like to set this to the size of two AXI bursts, so that
+ // while one is being read out the second can be read in. Can
+ // also be set larger if desired.
+ parameter LGFIFO = OPT_LGMAXBURST+1,
+ //
+ // AXI_ID is the ID we will use for all of our AXI transactions
+ parameter AXI_ID = 0,
+ //
+ // OPT_TUSER_IS_SOF. Xilinx and I chose different stream
+ // encodings. I encode TLAST== VLAST and
+ // TUSER == (optional) HLAST. Xilinx chose TLAST == HLAST
+ // and TUSER == SOF(start of frame). Set OPT_TUSER_IS_SOF to
+ // use Xilinx's encoding.
+ parameter [0:0] OPT_TUSER_IS_SOF = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The video stream interface
+ // {{{
+ output wire M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ output wire /* VLAST or HLAST */ M_AXIS_TLAST,
+ output wire /* HLAST or SOF */ M_AXIS_TUSER,
+ // }}}
+ //
+ // The control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ //
+
+ //
+ // The AXI (full) read interface
+ // {{{
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [7:0] M_AXI_ARLEN,
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [1:0] M_AXI_RRESP
+ // }}}
+ // }}}
+ );
+
+ // Core logic implementation
+ // {{{
+ // Local parameter declarations
+ // {{{
+ localparam [1:0] FBUF_CONTROL = 2'b00,
+ FBUF_FRAMEINFO = 2'b01,
+ FBUF_ADDRLO = 2'b10,
+ FBUF_ADDRHI = 2'b11;
+
+ localparam CBIT_ACTIVE = 0,
+ CBIT_BUSY = 1,
+ CBIT_ERR = 2,
+ CBIT_DIRTY = 3;
+
+ localparam TMPLGMAXBURST=(LGFIFO-1 > OPT_LGMAXBURST)
+ ? OPT_LGMAXBURST : LGFIFO-1;
+
+ localparam LGMAXBURST = (TMPLGMAXBURST+ADDRLSB > 12)
+ ? (12-ADDRLSB) : TMPLGMAXBURST;
+ // }}}
+
+ wire i_clk = S_AXI_ACLK;
+ wire i_reset = !S_AXI_ARESETN;
+
+ // Signal declarations
+ // {{{
+ reg soft_reset, r_err, r_stopped;
+ reg cfg_active, cfg_zero_length, cfg_dirty;
+ reg [C_AXI_ADDR_WIDTH-1:0] cfg_frame_addr;
+ reg [15:0] cfg_frame_lines, cfg_line_step;
+ reg [16-ADDRLSB-1:0] cfg_line_words;
+
+ // FIFO signals
+ wire reset_fifo, write_to_fifo,
+ read_from_fifo;
+ wire [C_AXI_DATA_WIDTH-1:0] write_data;
+ wire [LGFIFO:0] fifo_fill;
+ wire fifo_full, fifo_empty;
+
+ wire awskd_valid, axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ //
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire arskd_valid, axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+ reg [C_AXIL_DATA_WIDTH-1:0] w_status_word;
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, new_wideaddr;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi;
+ reg [C_AXIL_DATA_WIDTH-1:0] wide_config;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_config;
+
+ // reg partial_burst_requested;
+ // reg [LGMAXBURST-1:0] addralign;
+ // reg [LGFIFO:0] rd_uncommitted;
+ // reg [LGMAXBURST:0] initial_burstlen;
+ // reg [LGLENWA-1:0] rd_reads_remaining;
+ // reg rd_none_remaining,
+ // rd_last_remaining;
+
+ // wire realign_last_valid;
+
+ reg axi_arvalid, lag_start, phantom_start, start_burst,
+ ar_none_outstanding;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+ reg [LGMAXBURST:0] max_burst;
+ reg [7:0] axi_arlen;
+ reg [15:0] ar_bursts_outstanding;
+ //
+ wire vlast, hlast;
+ reg [15:0] r_frame_lines, r_line_step;
+ reg [16-ADDRLSB-1:0] r_line_words;
+
+ reg req_hlast, req_vlast;
+ reg [15:0] req_nlines;
+ reg [16-ADDRLSB-1:0] req_line_words;
+ reg [C_AXI_ADDR_WIDTH:0] req_addr, req_line_addr;
+ //
+ reg rd_hlast, rd_vlast;
+ reg [15:0] rd_lines;
+ reg [16-ADDRLSB-1:0] rd_line_beats;
+ wire no_fifo_space_available;
+ //
+ reg [LGMAXBURST-1:0] till_boundary;
+ reg [LGFIFO:0] fifo_space_available;
+
+
+`ifdef FORMAL
+ reg [C_AXI_ADDR_WIDTH:0] f_rd_line_addr, f_rd_addr;
+`endif
+
+ wire M_AXIS_VLAST, M_AXIS_HLAST;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // This is mostly the skidbuffer logic, and handling of the VALID
+ // and READY signals for the AXI-lite control logic in the next
+ // section.
+ // {{{
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite controlled logic
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ // soft_reset, r_err
+ // {{{
+ initial soft_reset = 1;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ soft_reset <= 1;
+ r_err <= 0;
+ end else if (soft_reset)
+ begin
+
+ if ((axil_write_ready && awskd_addr == FBUF_CONTROL)
+ && wskd_strb[0] && wskd_data[CBIT_ERR])
+ r_err <= cfg_zero_length;
+
+ if (cfg_active && !r_err && r_stopped)
+ soft_reset <= 0;
+
+ end else // if (!soft_reset)
+ begin
+ // Halt on any bus error. We'll require user intervention
+ // to start back up again
+ if (M_AXI_RREADY && M_AXI_RVALID && M_AXI_RRESP[1])
+ begin
+ soft_reset <= 1;
+ r_err <= 1;
+ end
+
+ // Halt on any user request
+ if (!cfg_active)
+ soft_reset <= 1;
+ end
+ // }}}
+
+ // wide_* and new_* write setup registers
+ // {{{
+ always @(*)
+ begin
+ wide_address = 0;
+ wide_address[C_AXI_ADDR_WIDTH-1:0] = cfg_frame_addr;
+ wide_address[ADDRLSB-1:0] = 0;
+
+ wide_config = { cfg_frame_lines, cfg_line_words,
+ {(ADDRLSB){1'b0}} };
+ end
+
+ assign new_cmdaddrlo = apply_wstrb(
+ wide_address[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+
+ generate if (C_AXI_ADDR_WIDTH > 32)
+ begin : GEN_LARGE_AW
+
+ assign new_cmdaddrhi = apply_wstrb(
+ wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+
+ end else begin : GEN_SINGLE_WORD_AW
+
+ assign new_cmdaddrhi = 0;
+
+ end endgenerate
+
+
+ wire [C_AXIL_DATA_WIDTH-1:0] new_control;
+ assign new_control = apply_wstrb(w_status_word, wskd_data, wskd_strb);
+ assign new_config = apply_wstrb(wide_config, wskd_data, wskd_strb);
+
+ always @(*)
+ begin
+ new_wideaddr = wide_address;
+
+ if (awskd_addr == FBUF_ADDRLO)
+ new_wideaddr[C_AXIL_DATA_WIDTH-1:0] = new_cmdaddrlo;
+ if (awskd_addr == FBUF_ADDRHI)
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH] = new_cmdaddrhi;
+ new_wideaddr[ADDRLSB-1:0] = 0;
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+ end
+ // }}}
+
+ // Configuration registers (Write)
+ // {{{
+ initial cfg_active = 0;
+ initial cfg_frame_addr = DEF_FRAMEADDR;
+ initial cfg_frame_addr[ADDRLSB-1:0] = 0;
+ initial cfg_line_words = DEF_WORDS_PER_LINE;
+ initial cfg_frame_lines = DEF_LINES_PER_FRAME;
+ initial cfg_zero_length = (DEF_WORDS_PER_LINE == 0)
+ ||(DEF_LINES_PER_FRAME == 0);
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ cfg_active <= DEF_ACTIVE_ON_RESET;
+ cfg_frame_addr <= DEF_FRAMEADDR;
+ cfg_line_words <= DEF_WORDS_PER_LINE;
+ cfg_line_step <= { DEF_WORDS_PER_LINE, {(ADDRLSB){1'b0}} };
+ cfg_frame_lines <= DEF_LINES_PER_FRAME;
+ cfg_zero_length <= (DEF_WORDS_PER_LINE==0)
+ ||(DEF_LINES_PER_FRAME == 0);
+ cfg_dirty <= 0;
+ end else begin
+ if (r_stopped)
+ cfg_dirty <= 0;
+ if (cfg_active && req_hlast && req_vlast && phantom_start)
+ cfg_dirty <= 0;
+ if (M_AXI_RREADY && M_AXI_RVALID && M_AXI_RRESP[1])
+ cfg_active <= 0;
+
+ if (axil_write_ready)
+ case(awskd_addr)
+ FBUF_CONTROL: begin
+ if (wskd_strb[0])
+ cfg_active <= wskd_data[CBIT_ACTIVE]
+ && (!r_err || wskd_data[CBIT_ERR])
+ && (!cfg_zero_length);
+
+ if (new_control[31:16] == 0)
+ begin
+ cfg_line_step <= 0;
+ cfg_line_step[16-1:ADDRLSB] <= cfg_line_words;
+ // Verilator lint_off WIDTH
+ cfg_dirty <= (cfg_line_step
+ != (cfg_line_words << ADDRLSB));
+ // Verilator lint_on WIDTH
+ end else begin
+ cfg_line_step <= new_control[31:16];
+ cfg_dirty <= (cfg_line_step != new_control[31:16]);
+ end end
+ FBUF_FRAMEINFO: begin
+ { cfg_frame_lines, cfg_line_words }
+ <= new_config[C_AXI_DATA_WIDTH-1:ADDRLSB];
+ cfg_zero_length <= (new_config[31:16] == 0)
+ ||(new_config[15:ADDRLSB] == 0);
+ if ((new_config[31:16] == 0)
+ ||(new_config[15:ADDRLSB] == 0))
+ cfg_active <= 0;
+ cfg_dirty <= 1;
+ end
+ FBUF_ADDRLO, FBUF_ADDRHI: begin
+ cfg_frame_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ cfg_dirty <= 1;
+ end
+ default: begin end
+ endcase
+ end
+ // }}}
+
+ // AXI-Lite read register data
+ // {{{
+ always @(*)
+ begin
+ w_status_word = 0;
+ w_status_word[31:16] = cfg_line_step;
+ w_status_word[CBIT_DIRTY] = cfg_dirty;
+ w_status_word[CBIT_ERR] = r_err;
+ w_status_word[CBIT_BUSY] = !soft_reset;
+ w_status_word[CBIT_ACTIVE] = cfg_active || (!soft_reset || !r_stopped);
+ end
+
+ always @(posedge i_clk)
+ if (!axil_read_valid || S_AXIL_RREADY)
+ begin
+ axil_read_data <= 0;
+ case(arskd_addr)
+ FBUF_CONTROL: axil_read_data <= w_status_word;
+ FBUF_FRAMEINFO: axil_read_data <= { cfg_frame_lines,
+ cfg_line_words, {(ADDRLSB){1'b0}} };
+ FBUF_ADDRLO: axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0];
+ FBUF_ADDRHI: axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ default axil_read_data <= 0;
+ endcase
+ end
+ // }}}
+
+ // apply_wstrb function for applying wstrbs to register words
+ // {{{
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The data FIFO section
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ // {{{
+ assign reset_fifo = r_stopped;
+
+ assign write_to_fifo = M_AXI_RVALID;
+ assign write_data = M_AXI_RDATA;
+ // assign realign_last_valid = 0;
+ assign vlast = rd_vlast;
+ assign hlast = rd_hlast;
+ assign M_AXI_RREADY = !fifo_full;
+ // }}}
+
+ generate if (LGFIFO > 0)
+ begin : GEN_SPACE_AVAILBLE
+ // Here's where we'll put the actual outgoing FIFO
+ // {{{
+ initial fifo_space_available = (1<<LGFIFO);
+ always @(posedge i_clk)
+ if (reset_fifo)
+ fifo_space_available <= (1<<LGFIFO);
+ else case({phantom_start, read_from_fifo && !fifo_empty })
+ 2'b00: begin end
+ // Verilator lint_off WIDTH
+ 2'b10: fifo_space_available <= fifo_space_available - (M_AXI_ARLEN+1);
+ 2'b11: fifo_space_available <= fifo_space_available - (M_AXI_ARLEN);
+ // Verilator lint_on WIDTH
+ 2'b01: fifo_space_available <= fifo_space_available + 1;
+ endcase
+
+ assign M_AXIS_TVALID = !fifo_empty;
+ assign read_from_fifo = M_AXIS_TVALID && M_AXIS_TREADY;
+
+ sfifo #(.BW(C_AXI_DATA_WIDTH+2), .LGFLEN(LGFIFO))
+ sfifo(i_clk, reset_fifo,
+ write_to_fifo, { vlast && hlast, hlast, write_data },
+ fifo_full, fifo_fill,
+ read_from_fifo, { M_AXIS_VLAST, M_AXIS_HLAST,
+ M_AXIS_TDATA }, fifo_empty);
+
+ assign no_fifo_space_available = (fifo_space_available < (1<<LGMAXBURST));
+ // }}}
+ end else begin : NO_FIFO
+ // {{{
+ assign fifo_full = !M_AXIS_TREADY;
+ assign fifo_fill = 0;
+ assign fifo_empty = !M_AXIS_TVALID;
+ assign M_AXIS_TVALID = write_to_fifo;
+
+ assign M_AXIS_VLAST = vlast && hlast;
+ assign M_AXIS_HLAST = hlast;
+ assign M_AXIS_TDATA = M_AXI_RDATA;
+
+ assign no_fifo_space_available = (ar_bursts_outstanding >= 3);
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Switch between TLAST/TUSER encodings if necessary
+ // {{{
+ generate if (OPT_TUSER_IS_SOF)
+ begin : XILINX_ENCODING
+
+ reg SOF;
+
+ initial SOF = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || r_stopped)
+ SOF <= 1'b1;
+ else if (M_AXIS_TVALID && M_AXIS_TREADY)
+ SOF <= M_AXIS_VLAST;
+
+ assign M_AXIS_TLAST = M_AXIS_HLAST;
+ assign M_AXIS_TUSER = SOF;
+
+ end else begin : VLAST_EQUALS_TLAST
+
+ assign M_AXIS_TLAST = M_AXIS_VLAST;
+ assign M_AXIS_TUSER = M_AXIS_HLAST;
+ end endgenerate
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outoing frame address counting
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || r_stopped || (phantom_start && req_hlast && req_vlast))
+ begin
+ r_frame_lines <= cfg_frame_lines;
+ r_line_words <= cfg_line_words;
+ r_line_step <= { {(ADDRLSB){1'b0}}, cfg_line_step[15:ADDRLSB] };
+ end
+
+ initial req_addr = 0;
+ initial req_line_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ req_addr <= { 1'b0, cfg_frame_addr };
+ req_line_addr <= { 1'b0, cfg_frame_addr };
+ req_line_words <= cfg_line_words;
+ end else if (phantom_start)
+ begin
+ if (req_hlast && req_vlast)
+ begin
+ req_addr <= { 1'b0, cfg_frame_addr };
+ req_line_addr <= { 1'b0, cfg_frame_addr };
+ req_line_words <= cfg_line_words;
+ end else if (req_hlast)
+ begin
+ // verilator lint_off WIDTH
+ req_addr <= req_line_addr
+ + (r_line_step << M_AXI_ARSIZE);
+ req_line_addr <= req_line_addr
+ + (r_line_step << M_AXI_ARSIZE);
+ // verilator lint_on WIDTH
+ req_line_words <= r_line_words;
+ end else begin
+ // verilator lint_off WIDTH
+ req_addr <= req_addr + (1<<(LGMAXBURST+ADDRLSB));
+ req_line_words <= req_line_words - (M_AXI_ARLEN+1);
+ // verilator lint_on WIDTH
+
+ req_addr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ end
+
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ req_nlines <= cfg_frame_lines-1;
+ req_vlast <= (cfg_frame_lines <= 1);
+ end else if (phantom_start && req_hlast)
+ begin
+ if (req_vlast)
+ begin
+ req_nlines <= cfg_frame_lines-1;
+ req_vlast <= (cfg_frame_lines <= 1);
+ end else begin
+ req_nlines <= req_nlines - 1;
+ req_vlast <= (req_nlines <= 1);
+ end
+ end
+`ifdef FORMAL
+ always @(*)
+ if (!r_stopped)
+ begin
+ assert(req_vlast == (req_nlines == 0));
+ assert(req_addr >= { 1'b0, cfg_frame_addr });
+ assert(req_line_addr >= { 1'b0, cfg_frame_addr });
+ assert(req_line_addr <= req_addr);
+ end
+
+ always @(*)
+ if (cfg_active)
+ begin
+ assert(cfg_frame_lines != 0);
+ assert(cfg_line_words != 0);
+ end
+
+ always @(*)
+ if (!r_stopped)
+ begin
+ assert(r_frame_lines != 0);
+ assert(r_line_words != 0);
+ end
+`endif
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming frame address counting
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // {{{
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ rd_line_beats <= cfg_line_words-1;
+ rd_hlast <= (cfg_line_words == 1);
+ end else if (M_AXI_RVALID && M_AXI_RREADY)
+ begin
+ if (rd_hlast)
+ begin
+ rd_line_beats <= r_line_words - 1;
+ rd_hlast <= (r_line_words <= 1);
+ end else begin
+ rd_line_beats <= rd_line_beats - 1;
+ rd_hlast <= (rd_line_beats <= 1);
+ end
+ end
+
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ rd_lines <= cfg_frame_lines-1;
+ rd_vlast <= (cfg_frame_lines == 1);
+ end else if (M_AXI_RVALID && M_AXI_RREADY && rd_hlast)
+ begin
+ if (vlast)
+ begin
+ rd_lines <= r_frame_lines - 1;
+ rd_vlast <= (r_frame_lines <= 1);
+ end else begin
+ rd_lines <= rd_lines - 1;
+ rd_vlast <= (rd_lines <= 1);
+ end
+ end
+
+`ifdef FORMAL
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ begin
+ f_rd_addr <= { 1'b0, cfg_frame_addr };
+ f_rd_line_addr <= { 1'b0, cfg_frame_addr };
+ end else if (M_AXI_RVALID && M_AXI_RREADY)
+ begin
+ if (rd_vlast && rd_hlast)
+ begin
+ f_rd_addr <= cfg_frame_addr;
+ f_rd_line_addr <= cfg_frame_addr;
+ end else if (rd_hlast)
+ begin
+ f_rd_addr <= f_rd_line_addr + (r_line_step << M_AXI_ARSIZE);
+ f_rd_line_addr <= f_rd_line_addr + (r_line_step << M_AXI_ARSIZE);
+ end else begin
+ f_rd_addr <= f_rd_addr + (1<<ADDRLSB);
+ end
+ end
+`endif
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The incoming AXI (full) protocol section
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // {{{
+
+ // Some counters to keep track of our state
+ // {{{
+
+ // ar_bursts_outstanding
+ // {{{
+ // Count the number of bursts outstanding--these are the number of
+ // ARVALIDs that have been accepted, but for which the RVALID && RLAST
+ // has not (yet) been returned.
+ initial ar_none_outstanding = 1;
+ initial ar_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ ar_bursts_outstanding <= 0;
+ ar_none_outstanding <= 1;
+ end else case ({ phantom_start,
+ M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST })
+ 2'b01: begin
+ ar_bursts_outstanding <= ar_bursts_outstanding - 1;
+ ar_none_outstanding <= (ar_bursts_outstanding == 1);
+ end
+ 2'b10: begin
+ ar_bursts_outstanding <= ar_bursts_outstanding + 1;
+ ar_none_outstanding <= 0;
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // r_stopped
+ // {{{
+ // Following an error or drop of cfg_active, soft_reset will go high.
+ // We then come to a stop once everything becomes inactive
+ initial r_stopped = 1;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_stopped <= 1;
+ else if (r_stopped)
+ r_stopped <= soft_reset || !cfg_active;
+ else if (soft_reset && ar_none_outstanding && !M_AXI_ARVALID)
+ r_stopped <= 1;
+ // }}}
+
+ // }}}
+
+ //
+ // start_burst
+ // {{{
+ always @(*)
+ begin
+ start_burst = 1;
+ if (no_fifo_space_available)
+ start_burst = 0;
+ if (phantom_start || lag_start)
+ // Insist on a minimum of two clocks between burst
+ // starts, so we can get our lengths right
+ start_burst = 0;
+
+ // Can't start a new burst if the outgoing channel is still
+ // stalled.
+ if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ start_burst = 0;
+
+ // If the user wants us to stop, then stop
+ if (!cfg_active || soft_reset)
+ start_burst = 0;
+ end
+ // }}}
+
+ // ARLEN
+ // {{{
+ // Coming in, req_addr and req_line_words can be trusted
+ // lag_start will be true to reflect this
+ always @(posedge i_clk)
+ if (lag_start)
+ begin
+ if (req_line_words >= (1<<LGMAXBURST))
+ max_burst <= (1<<LGMAXBURST);
+ else
+ // Verilator lint_off WIDTH
+ max_burst <= req_line_words;
+ // Verilator lint_on WIDTH
+ end
+
+ always @(*)
+ till_boundary = ~req_addr[ADDRLSB +: LGMAXBURST];
+
+ always @(posedge i_clk)
+ if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+ // Verilator lint_off WIDTH
+ if (till_boundary > 0 && max_burst <= till_boundary)
+ axi_arlen <= max_burst-1;
+ // Verilator lint_on WIDTH
+ else
+ axi_arlen <= till_boundary;
+ end
+ // }}}
+
+ // req_hlast
+ // {{{
+ always @(posedge i_clk)
+ if (lag_start || start_burst)
+ begin
+ req_hlast <= 1;
+ // Verilator lint_off WIDTH
+ if (req_line_words > till_boundary+1)
+ req_hlast <= 0;
+ if (req_line_words > max_burst)
+ req_hlast <= 0;
+ // Verilator lint_on WIDTH
+ end
+
+`ifdef FORMAL
+ always @(*)
+ if (phantom_start)
+ begin
+ if (req_hlast)
+ begin
+ assert(axi_arlen+1 == req_line_words);
+ end else
+ assert(axi_arlen+1 < req_line_words);
+ end else if (!soft_reset && !r_stopped && !lag_start)
+ begin
+ if (max_burst != req_line_words)
+ begin
+ assert(!req_hlast);
+ end else if (!req_hlast && !M_AXI_ARVALID)
+ assert(axi_arlen < max_burst);
+
+ assert(max_burst > 0);
+ if (req_line_words > (1<<LGMAXBURST))
+ begin
+ assert(max_burst == (1<<LGMAXBURST));
+ end else
+ assert(max_burst == req_line_words);
+ end
+`endif
+ // }}}
+
+ // phantom_start
+ // {{{
+ initial phantom_start = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ phantom_start <= 0;
+ else
+ phantom_start <= start_burst;
+
+ initial lag_start = 1;
+ always @(posedge i_clk)
+ if (i_reset || r_stopped)
+ lag_start <= 1;
+ else
+ lag_start <= phantom_start;
+ // }}}
+
+ // ARVALID
+ // {{{
+ initial axi_arvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_arvalid <= 0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arvalid <= start_burst;
+ // }}}
+
+ // ARADDR
+ // {{{
+ initial axi_araddr = 0;
+ always @(posedge i_clk)
+ begin
+ if (start_burst)
+ axi_araddr <= req_addr[C_AXI_ADDR_WIDTH-1:0];
+
+ axi_araddr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Set the constant M_AXI_* signals
+ // {{{
+ assign M_AXI_ARVALID= axi_arvalid;
+ assign M_AXI_ARID = AXI_ID;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARLEN = axi_arlen;
+ // Verilator lint_off WIDTH
+ assign M_AXI_ARSIZE = $clog2(C_AXI_DATA_WIDTH)-3;
+ // Verilator lint_on WIDTH
+ assign M_AXI_ARBURST= 2'b01; // INCR
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE= 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_ARQOS = 0;
+ // }}}
+
+ // End AXI protocol section
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_RID,
+ M_AXI_RRESP[0], fifo_full, wskd_strb[2:0],
+ S_AXIL_AWADDR[AXILLSB-1:0], S_AXIL_ARADDR[AXILLSB-1:0],
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_control, new_config, fifo_fill };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+//
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The following are only a subset of the formal properties currently
+ // being used to verify this module. They are not expected to be
+ // syntactically accurate.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Properties of the AXI-stream data interface
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // (These are captured by the FIFO within)
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding, faxil_awr_outstanding;
+
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_rd_outstanding == (S_AXIL_RVALID ? 1:0)
+ +(S_AXIL_ARREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_WREADY ? 0:1));
+ assert(faxil_awr_outstanding== (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_AWREADY ? 0:1));
+ end
+
+ always @(*)
+ assert(cfg_zero_length ==
+ ((cfg_line_words == 0)||(cfg_frame_lines == 0)));
+
+ always @(*)
+ if (cfg_zero_length)
+ assert(!cfg_active);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI master memory interface
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ localparam F_AXI_LGDEPTH = 11; // LGLENW-LGMAXBURST+2 ??
+
+ //
+ // ...
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ //
+ // ...
+ //
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ // The (unused) write interface
+ // {{{
+ .i_axi_awvalid(1'b0),
+ .i_axi_awready(1'b0),
+ .i_axi_awid( AXI_ID),
+ .i_axi_awaddr( 0),
+ .i_axi_awlen( 0),
+ .i_axi_awsize( 0),
+ .i_axi_awburst(0),
+ .i_axi_awlock( 0),
+ .i_axi_awcache(0),
+ .i_axi_awprot( 0),
+ .i_axi_awqos( 0),
+ //
+ .i_axi_wvalid(0),
+ .i_axi_wready(0),
+ .i_axi_wdata( 0),
+ .i_axi_wstrb( 0),
+ .i_axi_wlast( 0),
+ //
+ .i_axi_bvalid(1'b0),
+ .i_axi_bready(1'b0),
+ .i_axi_bid( AXI_ID),
+ .i_axi_bresp( 2'b00),
+ // }}}
+ // The read interface
+ // {{{
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_arid( M_AXI_ARID),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arlen( M_AXI_ARLEN),
+ .i_axi_arsize( M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock( M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot( M_AXI_ARPROT),
+ .i_axi_arqos( M_AXI_ARQOS),
+ //
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rlast( M_AXI_RLAST),
+ .i_axi_rresp( M_AXI_RRESP),
+ // ...
+ // }}}
+ );
+
+
+ //
+ // ...
+ //
+ always @(*)
+ begin
+ // ...
+
+ assert(M_AXI_ARBURST == 2'b01);
+ end
+
+ always @(*)
+ if (faxi_rd_ckvalid)
+ begin
+ assert(!r_stopped);
+ //
+ // ...
+ //
+ end
+
+ //
+ // ...
+ //
+
+ reg [LGMAXBURST-1:0] f_rd_subaddr;
+ always @(*)
+ f_rd_subaddr = f_rd_addr[ADDRLSB +: LGMAXBURST];
+
+ always @(*)
+ begin
+ assert(cfg_frame_addr[ADDRLSB-1:0] == 0);
+ if (!r_stopped)
+ begin
+ assert(req_addr[ADDRLSB-1:0] == 0);
+ assert(req_line_addr[ADDRLSB-1:0] == 0);
+ end
+
+ if (M_AXI_RVALID)
+ assert(M_AXI_RLAST == (rd_hlast || (&f_rd_subaddr)));
+ end
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read request to return address correlations
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ if (M_AXI_RVALID)
+ begin
+ if (rd_hlast || (&f_rd_subaddr))
+ begin
+ // ...
+ assert(M_AXI_RLAST);
+ end else
+ // ...
+ assume(!M_AXI_RLAST);
+ end
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Other formal properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ reg cvr_full_frame, cvr_full_size;
+ (* anyconst *) reg cvr_hlast_rlast;
+
+ always @(posedge i_clk)
+ if (r_stopped)
+ cvr_full_size <= (cfg_line_step >= cfg_line_words)
+ &&(cfg_line_words > 5)
+ &&(cfg_frame_lines > 4);
+ else begin
+ if ($changed(cfg_line_step))
+ cvr_full_size <= 0;
+ if ($changed(cfg_line_words))
+ cvr_full_size <= 0;
+ if ($changed(cfg_frame_lines))
+ cvr_full_size <= 0;
+ if ($changed(cfg_frame_addr))
+ cvr_full_size <= 0;
+ end
+
+ always @(posedge i_clk)
+ if (r_stopped || !cvr_full_size)
+ cvr_full_frame <= 0;
+ else if (!cvr_full_frame)
+ cvr_full_frame <= rd_vlast && rd_hlast && M_AXI_RVALID && M_AXI_RREADY;
+
+ always @(*)
+ cover(!soft_reset);
+
+ always @(*)
+ cover(start_burst);
+
+ always @(*)
+ cover(M_AXI_ARVALID && M_AXI_ARREADY);
+
+ always @(*)
+ cover(M_AXI_RVALID);
+
+ always @(*)
+ cover(M_AXI_RVALID & M_AXI_RLAST);
+
+ always @(*)
+ cover(!r_stopped && cvr_full_frame);
+
+ always @(*)
+ cover(cvr_full_frame && phantom_start && !r_stopped);
+
+ always @(*)
+ if (cvr_hlast_rlast)
+ begin
+ // assume(M_AXI_RLAST == (rd_hlast && M_AXI_RVALID));
+ assume(M_AXI_ARREADY && M_AXI_RREADY);
+ assume(M_AXIS_TREADY);
+ assume(cfg_frame_addr[12:0] == 0);
+ assume(cfg_line_step[3:0] == 0);
+ end
+
+ always @(*)
+ cover(cvr_hlast_rlast && cvr_full_frame && phantom_start && !r_stopped);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Simplifying (careless) assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge i_clk)
+ if (!r_stopped || cfg_active)
+ begin
+ assume($stable(cfg_frame_addr));
+ assume($stable(cfg_frame_lines));
+ assume($stable(cfg_line_words));
+ assume($stable(cfg_line_step));
+ end
+
+
+ always @(*)
+ assume(!f_sequential);
+
+ always @(*)
+ assume(!f_biglines);
+
+ always @(*)
+ assume(!req_addr[C_AXI_ADDR_WIDTH]);
+
+ always @(*)
+ assume(!req_line_addr[C_AXI_ADDR_WIDTH]);
+ // }}}
+ // }}}
+`endif
+endmodule
diff --git a/rtl/wb2axip/axivfifo.v b/rtl/wb2axip/axivfifo.v
new file mode 100644
index 0000000..2dc5369
--- /dev/null
+++ b/rtl/wb2axip/axivfifo.v
@@ -0,0 +1,1405 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axivfifo.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A virtual FIFO, using an AXI based memory on the back end. Data
+// written via the AXI stream interface is written to an external
+// memory once enough is available to fill a burst. It's then copied from
+// this external memory to a FIFO from which it can drive an outgoing
+// stream.
+//
+// Registers:
+// This core is simple--providing no control interface nor registers
+// whereby it may be controlled. To place it in a particular region of
+// SDRAM, limit the address width and fill the rest of the address with
+// the region you want. Note: THIS CORE DEPENDS UPON ALIGNED MEMORY
+// ACCESSES. Hence, it must be aligned to the memory to keep these
+// accesses aligned.
+//
+// Performance goals:
+// 100% throughput
+// Stay off the bus until you can drive it hard
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+//
+// `define AXI3
+// }}}
+module axivfifo #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ //
+ // This core requires that the stream data width be identical
+ // to the bus data width. Use an upstream core if you need to
+ // pack a smaller width into your bus's width, or a downstream
+ // core if you need to unpack it.
+ localparam C_AXIS_DATA_WIDTH = C_AXI_DATA_WIDTH,
+ //
+ // LGMAXBURST determines the size of the maximum AXI burst.
+ // In AXI4, the maximum burst size is 256 beats the log_2()
+ // of which is 8. In AXI3, it's a 16 beat burst of which the
+ // log_2() is 4. Smaller numbers are also permissible here,
+ // although not verified. I expect problems if LGMAXBURST is
+ // ever set to zero (no bursting). An upgrade should fix that.
+ // Lower LGMAXBURST values will decrease the latency in this
+ // core while possibly causing throughput to be decreased
+ // (in the rest of the system--this core can handle 100%
+ // throughput either way.)
+ //
+ // Beware of the AXI requirement that bursts cannot cross
+ // 4kB boundaries. If your bus is larger than 128 bits wide,
+ // you'll need to lower this maximum burst size to meet that
+ // requirement.
+`ifdef AXI3
+ parameter LGMAXBURST=4, // 16 beats max
+`else
+ parameter LGMAXBURST=8, // 256 beats
+`endif
+ //
+ // LGFIFO: This is the (log-based-2) size of the internal FIFO.
+ // Hence if LGFIFO=8, the internal FIFO will have 256 elements
+ // (words) in it. High throughput transfers are accomplished
+ // by first storing data into a FIFO, then once a full burst
+ // size is available bursting that data over the bus. In
+ // order to be able to keep receiving data while bursting it
+ // out, the FIFO size must be at least twice the size of the
+ // maximum burst size--that is LGFIFO must be at least one more
+ // than LGMAXBURST. Larger sizes are possible as well.
+ parameter LGFIFO = LGMAXBURST+1, // 512 element FIFO
+ //
+ // AXI uses ID's to transfer information. This core rather
+ // ignores them. Instead, it uses a constant ID for all
+ // transfers. The following two parameters control that ID.
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_READ_ID = 0,
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_WRITE_ID = 0,
+ //
+ localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI4-stream interface
+ // {{{
+ // This core does not support TLAST, TKEEP, or TSTRB. If you
+ // want to support these extra values, expand the width of
+ // TDATA, and unpack them on the output of the FIFO.
+ //
+ // The incoming stream
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ //
+ // The outgoing stream
+ output wire M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ // }}}
+ //
+ // The AXI Master (DMA) interface
+ // {{{
+ // First to write data to the (external) AXI buffer
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_AWLEN,
+`else
+ output wire [7:0] M_AXI_AWLEN,
+`endif
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_AWLOCK,
+`else
+ output wire M_AXI_AWLOCK,
+`endif
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+`ifdef AXI3
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+`endif
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ // Then the read interface to read the data back
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_ARLEN,
+`else
+ output wire [7:0] M_AXI_ARLEN,
+`endif
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_ARLOCK,
+`else
+ output wire M_AXI_ARLOCK,
+`endif
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP,
+ // }}}
+ //
+ // {{{
+ // Request a soft-reset: reset the FIFO without resetting th bus
+ input wire i_reset,
+ // o_err is a hard error. If ever true, the core will come
+ // to a hard stop.
+ output reg o_err,
+ // o_overflow is an indication of data changing before it is
+ // accepted.
+ output reg o_overflow,
+ // o_mm2s_full is a reference to the last FIFO in our
+ // processing pipeline. It's true if a burst of (1<<LGMAXBURST)
+ // words of (1<<ADDRLSB) bytes may be read from the downstream
+ // FIFO without waiting on the external memory.
+ output reg o_mm2s_full,
+ // o_empty is true if nothing is in the core.
+ output reg o_empty,
+ // o_fill counts the number of items in the core. Just because
+ // the number of items is non-zero, however, doesn't mean you
+ // can read them out. In general, you will need to write at
+ // least a full (1<<LGMAXBURST) words to the core, those will
+ // need to be written to memory, read from memory, and then
+ // used to fill the downstream FIFO before you can read. This
+ // number is just available for your informational use.
+ output reg [C_AXI_ADDR_WIDTH-ADDRLSB:0] o_fill
+ // }}}
+ // }}}
+ );
+
+ // Register and signal definitions
+ // {{{
+ // The number of beats in this maximum burst size is automatically
+ // determined from LGMAXBURST, and so its forced to be a power of
+ // two this way.
+ localparam MAXBURST=(1<<LGMAXBURST);
+ localparam BURSTAW = C_AXI_ADDR_WIDTH-LGMAXBURST-ADDRLSB;
+
+ reg soft_reset, vfifo_empty, vfifo_full;
+ wire reset_fifo;
+ reg [C_AXI_ADDR_WIDTH-ADDRLSB:0] vfifo_fill;
+ reg [BURSTAW:0] mem_data_available_w,
+ writes_outstanding;
+ reg [BURSTAW:0] mem_space_available_w,
+ reads_outstanding;
+ reg s_last_stalled;
+ reg [C_AXI_DATA_WIDTH-1:0] s_last_tdata;
+ wire read_from_fifo, ififo_full, ififo_empty;
+ wire [C_AXI_DATA_WIDTH-1:0] ififo_data;
+ wire [LGFIFO:0] ififo_fill;
+
+ reg start_write, phantom_write,
+ axi_awvalid, axi_wvalid, axi_wlast,
+ writes_idle;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [LGMAXBURST:0] writes_pending;
+
+ reg start_read, phantom_read, reads_idle,
+ axi_arvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+
+ reg skd_valid;
+ reg [C_AXI_DATA_WIDTH-1:0] skd_data;
+
+ reg [LGFIFO:0] ofifo_space_available;
+ wire write_to_fifo, ofifo_empty, ofifo_full;
+ wire [LGFIFO:0] ofifo_fill;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Global FIFO signal handling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // A soft reset
+ // {{{
+ // This is how we reset the FIFO without resetting the rest of the AXI
+ // bus. On a reset request, we raise the soft_reset flag and reset all
+ // of our internal FIFOs. We also stop issuing bus commands. Once all
+ // outstanding bus commands come to a halt, then we release from reset
+ // and start operating as a FIFO.
+ initial soft_reset = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ soft_reset <= 0;
+ else if (i_reset)
+ soft_reset <= 1;
+ else if (writes_idle && reads_idle)
+ soft_reset <= 0;
+
+ assign reset_fifo = soft_reset || !S_AXI_ARESETN;
+ // }}}
+
+ //
+ // Calculating the fill of the virtual FIFO, and the associated
+ // full/empty flags that go with it
+ // {{{
+ initial vfifo_fill = 0;
+ initial vfifo_empty = 1;
+ initial vfifo_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ begin
+ vfifo_fill <= 0;
+ vfifo_empty <= 1;
+ vfifo_full <= 0;
+ end else case({ S_AXIS_TVALID && S_AXIS_TREADY,
+ M_AXIS_TVALID && M_AXIS_TREADY })
+ 2'b10: begin
+ vfifo_fill <= vfifo_fill + 1;
+ vfifo_empty <= 0;
+ vfifo_full <= (&vfifo_fill[C_AXI_ADDR_WIDTH-ADDRLSB-1:0]);
+ end
+ 2'b01: begin
+ vfifo_fill <= vfifo_fill - 1;
+ vfifo_full <= 0;
+ vfifo_empty<= (vfifo_fill <= 1);
+ end
+ default: begin end
+ endcase
+
+ always @(*)
+ o_fill = vfifo_fill;
+
+ always @(*)
+ o_empty = vfifo_empty;
+ // }}}
+
+ // Determining when the write half is idle
+ // {{{
+ // This is required to know when to come out of soft reset.
+ //
+ // The first step is to count the number of bursts that remain
+ // outstanding
+ initial writes_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_outstanding <= 0;
+ else case({ phantom_write,M_AXI_BVALID && M_AXI_BREADY})
+ 2'b01: writes_outstanding <= writes_outstanding - 1;
+ 2'b10: writes_outstanding <= writes_outstanding + 1;
+ default: begin end
+ endcase
+
+ // The second step is to use this counter to determine if we are idle.
+ // If WVALID is ever high, or start_write goes high, then we are
+ // obviously not idle. Otherwise, we become idle when the number of
+ // writes outstanding transitions to (or equals) zero.
+ initial writes_idle = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_idle <= 1;
+ else if (start_write || M_AXI_WVALID)
+ writes_idle <= 0;
+ else
+ writes_idle <= (writes_outstanding
+ == ((M_AXI_BVALID && M_AXI_BREADY) ? 1:0));
+ // }}}
+
+ // Count how much space is used in the memory device
+ // {{{
+ // Well, obviously, we can't fill our memory device or we have problems.
+ // To make sure we don't overflow, we count memory usage here. We'll
+ // count memory usage in units of bursts of (1<<LGMAXBURST) words of
+ // (1<<ADDRLSB) bytes each. So ... here we count the amount of device
+ // memory that hasn't (yet) been committed. This is different from the
+ // memory used (which we don't calculate), or the memory which may yet
+ // be read--which we'll calculate in a moment.
+ initial mem_space_available_w = (1<<BURSTAW);
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ mem_space_available_w <= (1<<BURSTAW);
+ else case({ phantom_write,M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST })
+ 2'b01: mem_space_available_w <= mem_space_available_w + 1;
+ 2'b10: mem_space_available_w <= mem_space_available_w - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // Determining when the read half is idle
+ // {{{
+ // Count the number of read bursts that we've committed to. This
+ // includes bursts that have ARVALID but haven't been accepted, as well
+ // as any the downstream device will yet return an RLAST for.
+ initial reads_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ reads_outstanding <= 0;
+ else case({ phantom_read,M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST})
+ 2'b01: reads_outstanding <= reads_outstanding - 1;
+ 2'b10: reads_outstanding <= reads_outstanding + 1;
+ default: begin end
+ endcase
+
+ // Now, using the reads_outstanding counter, we can check whether or not
+ // we are idle (and can exit a reset) of if instead there are more
+ // bursts outstanding to wait for.
+ //
+ // By registering this counter, we can keep the soft_reset release
+ // simpler. At least this way, it doesn't need to check two counters
+ // for zero.
+ initial reads_idle = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ reads_idle <= 1;
+ else if (start_read || M_AXI_ARVALID)
+ reads_idle <= 0;
+ else
+ reads_idle <= (reads_outstanding
+ == ((M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST) ? 1:0));
+ // }}}
+
+ // Count how much data is in the memory device that we can read out
+ // {{{
+ // In AXI, after you issue a write, you can't depend upon that data
+ // being present on the device and available for a read until the
+ // associated BVALID is returned. Therefore we don't count any memory
+ // as available to be read until BVALID comes back. Once a read
+ // command is issued, the memory is again no longer available to be
+ // read. Note also that we are counting bursts here. A second
+ // conversion below converts this count to bytes.
+ initial mem_data_available_w = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ mem_data_available_w <= 0;
+ else case({ M_AXI_BVALID, phantom_read })
+ 2'b10: mem_data_available_w <= mem_data_available_w + 1;
+ 2'b01: mem_data_available_w <= mem_data_available_w - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Error detection
+ // {{{
+ initial o_err = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ o_err <= 1'b0;
+ else begin
+ if (M_AXI_BVALID && M_AXI_BRESP[1])
+ o_err <= 1'b1;
+ if (M_AXI_RVALID && M_AXI_RRESP[1])
+ o_err <= 1'b1;
+ end
+ // }}}
+
+ //
+ // Incoming stream overflow detection
+ // {{{
+ // The overflow flag is set if ever an incoming value violates the
+ // stream protocol and changes while stalled. Internally, however,
+ // the overflow flag is ignored. It's provided for your information.
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_last_stalled <= 0;
+ else
+ s_last_stalled <= S_AXIS_TVALID && !S_AXIS_TREADY;
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXIS_TVALID)
+ s_last_tdata <= S_AXIS_TDATA;
+
+ initial o_overflow = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ o_overflow <= 0;
+ else if (s_last_stalled)
+ begin
+ if (!S_AXIS_TVALID)
+ o_overflow <= 1;
+ if (S_AXIS_TDATA != s_last_tdata)
+ o_overflow <= 1;
+ end
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming FIFO
+ // {{{
+ // Incoming data stream info the FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ assign S_AXIS_TREADY = !reset_fifo && !ififo_full && !vfifo_full;
+ assign read_from_fifo= (!skd_valid || (M_AXI_WVALID && M_AXI_WREADY));
+
+ sfifo #(.BW(C_AXIS_DATA_WIDTH), .LGFLEN(LGFIFO))
+ ififo(S_AXI_ACLK, reset_fifo,
+ S_AXIS_TVALID && S_AXIS_TREADY,
+ S_AXIS_TDATA, ififo_full, ififo_fill,
+ read_from_fifo, ififo_data, ififo_empty);
+
+ //
+ // We need a quick 1-element buffer here in order to keep the soft
+ // reset, which resets the FIFO pointer, from adjusting any FIFO data.
+ // {{{
+ // Here's the rule: we need to fill the buffer before it ever gets
+ // used. Then, once used, it should be able to maintain 100%
+ // throughput.
+ initial skd_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (reset_fifo)
+ skd_valid <= 0;
+ else if (!ififo_empty)
+ skd_valid <= 1;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ skd_valid <= 0;
+
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (!skd_valid || M_AXI_WREADY)
+ skd_data <= ififo_data;
+ end
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI write processing
+ // {{{
+ // Write data from our FIFO onto the bus
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // start_write: determining when to issue a write burst
+ // {{{
+ always @(*)
+ begin
+ start_write = 0;
+
+ if (ififo_fill >= (1<<LGMAXBURST))
+ start_write = 1;
+ if (vfifo_full || soft_reset || phantom_write)
+ start_write = 0;
+ if (mem_space_available_w == 0)
+ start_write = 0;
+
+ if (M_AXI_WVALID && (!M_AXI_WREADY || !M_AXI_WLAST))
+ start_write = 0;
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ start_write = 0;
+ if (o_err)
+ start_write = 0;
+ end
+ // }}}
+
+ // Register the start write signal into AWVALID and phantom write
+ // {{{
+ // phantom_write contains the start signal, but immediately clears
+ // on the next clock cycle. This allows us some time to calculate
+ // the data for the next burst which and if AWVALID remains high and
+ // not yet accepted.
+ initial phantom_write = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ phantom_write <= 0;
+ else
+ phantom_write <= start_write;
+
+ // Set AWVALID to start_write if every the channel isn't stalled.
+ // Incidentally, start_write is guaranteed to be zero if the channel
+ // is stalled, since that signal is used by other things as well.
+ initial axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awvalid <= start_write;
+ // }}}
+
+ // Write address
+ // {{{
+ // We insist on alignment. On every accepted burst, we step forward by
+ // one burst length. On reset, we reset the address at our first
+ // opportunity.
+ initial axi_awaddr = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (M_AXI_AWVALID && M_AXI_AWREADY)
+ axi_awaddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]
+ <= axi_awaddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB] +1;
+
+ if ((!M_AXI_AWVALID || M_AXI_AWREADY) && soft_reset)
+ axi_awaddr <= 0;
+
+ if (!S_AXI_ARESETN)
+ axi_awaddr <= 0;
+
+ axi_awaddr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Write data channel valid
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_wvalid <= 0;
+ else if (start_write)
+ axi_wvalid <= 1;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wvalid <= M_AXI_WVALID && !M_AXI_WLAST;
+ // }}}
+
+ // WLAST generation
+ // {{{
+ // On the beginning of any burst, start a counter of the number of items
+ // in it. Once the counter gets to 1, set WLAST.
+ initial writes_pending = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_pending <= 0;
+ else if (start_write)
+ writes_pending <= MAXBURST;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ writes_pending <= writes_pending -1;
+
+ always @(posedge S_AXI_ACLK)
+ if (start_write)
+ axi_wlast <= (LGMAXBURST == 0);
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wlast <= (writes_pending == 1 + (M_AXI_WVALID ? 1:0));
+ // }}}
+
+ // Bus assignments based upon the above
+ // {{{
+ assign M_AXI_AWVALID = axi_awvalid;
+ assign M_AXI_AWID = AXI_WRITE_ID;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = MAXBURST-1;
+ assign M_AXI_AWSIZE = ADDRLSB[2:0];
+ assign M_AXI_AWBURST = 2'b01;
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE = 0;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WDATA = skd_data;
+`ifdef AXI3
+ assign M_AXI_WID = AXI_WRITE_ID;
+`endif
+ assign M_AXI_WLAST = axi_wlast;
+ assign M_AXI_WSTRB = -1;
+
+ assign M_AXI_BREADY = 1;
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI read processing
+ // {{{
+ // Read data into our FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // How much FIFO space is available?
+ // {{{
+ // One we issue a read command, the FIFO space isn't available any more.
+ // That way we can determine when a second read can be issued--even
+ // before the first has returned--while also guaranteeing that there's
+ // always room in the outgoing FIFO for anything that might return.
+ // Remember: NEVER generate backpressure in a bus master
+ initial ofifo_space_available = (1<<LGFIFO);
+ always @(posedge S_AXI_ACLK)
+ if (reset_fifo)
+ ofifo_space_available <= (1<<LGFIFO);
+ else case({phantom_read, M_AXIS_TVALID && M_AXIS_TREADY})
+ 2'b10: ofifo_space_available <= ofifo_space_available - MAXBURST;
+ 2'b01: ofifo_space_available <= ofifo_space_available + 1;
+ 2'b11: ofifo_space_available <= ofifo_space_available - MAXBURST + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // Determine when to start a next read-from-memory-to-FIFO burst
+ // {{{
+ always @(*)
+ begin
+ start_read = 1;
+
+ // We can't read yet if we don't have space available.
+ // Note the comparison is carefully chosen to make certain
+ // it doesn't use all ofifo_space_available bits, but rather
+ // only the number of bits between LGFIFO and
+ // LGMAXBURST--nominally a single bit.
+ if (ofifo_space_available < MAXBURST) // FIFO space ?
+ start_read = 0;
+
+ // If there's no memory available for us to read from, then
+ // we can't start a read yet.
+ if (!M_AXI_BVALID && mem_data_available_w == 0)
+ start_read = 0;
+
+ // Don't start anything while waiting on a reset. Likewise,
+ // insist on a minimum one clock between read burst issuances.
+ if (soft_reset || phantom_read)
+ start_read = 0;
+
+ // We can't start a read request if the AR* channel is stalled
+ if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ start_read = 0;
+
+ // Following a bus error, we come to a complete halt. Such a
+ // bus error is an indication that something *SERIOUSLY* went
+ // wrong--perhaps we aren't accessing the memory we are supposed
+ // to. To prevent damage to external devices, we disable
+ // ourselves entirely. There is no fall back. We only
+ // restart on a full bus restart.
+ if (o_err)
+ start_read = 0;
+ end
+ // }}}
+
+ // Set phantom_read and ARVALID
+ // {{{
+ initial phantom_read = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ phantom_read <= 0;
+ else
+ phantom_read <= start_read;
+
+ initial axi_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arvalid <= 0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arvalid <= start_read;
+ // }}}
+
+ // Calculate the next ARADDR
+ // {{{
+ initial axi_araddr = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (M_AXI_ARVALID && M_AXI_ARREADY)
+ axi_araddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]
+ <= axi_araddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]+ 1;
+
+ if ((!M_AXI_ARVALID || M_AXI_ARREADY) && soft_reset)
+ axi_araddr <= 0;
+
+ if (!S_AXI_ARESETN)
+ axi_araddr <= 0;
+
+ axi_araddr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Assign values to our bus wires
+ // {{{
+ assign M_AXI_ARVALID = axi_arvalid;
+ assign M_AXI_ARID = AXI_READ_ID;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARLEN = MAXBURST-1;
+ assign M_AXI_ARSIZE = ADDRLSB[2:0];
+ assign M_AXI_ARBURST = 2'b01;
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE = 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_ARQOS = 0;
+
+ assign M_AXI_RREADY = 1;
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing AXI stream processing
+ // {{{
+ // Send data out from the MM2S FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // We basically just stuff the data read from memory back into our
+ // outgoing FIFO here. The logic is quite straightforward.
+ assign write_to_fifo = M_AXI_RVALID && M_AXI_RREADY;
+ assign M_AXIS_TVALID = !ofifo_empty;
+
+ sfifo #(.BW(C_AXIS_DATA_WIDTH), .LGFLEN(LGFIFO))
+ ofifo(S_AXI_ACLK, reset_fifo,
+ write_to_fifo,
+ M_AXI_RDATA, ofifo_full, ofifo_fill,
+ M_AXIS_TVALID && M_AXIS_TREADY, M_AXIS_TDATA, ofifo_empty);
+
+ always @(*)
+ o_mm2s_full = |ofifo_fill[LGFIFO:LGMAXBURST];
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, M_AXI_BID, M_AXI_RID,
+ M_AXI_BRESP[0], M_AXI_RRESP[0],
+ ififo_empty, ofifo_full, ofifo_fill
+ // fifo_full, fifo_fill, fifo_empty,
+ };
+
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+//
+// The following are a subset of the formal properties used to verify this
+// core. The full set of formal properties, together with the formal
+// property set itself, are available for purchase.
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // FAXI_DEPTH controls the width of the counters in the bus property
+ // interface file. In order to support bursts of length 8 (or more),
+ // there's a minimum of 9. Otherwise, we'll just set this to the width
+ // of the AXI address bus.
+ localparam FAXI_DEPTH = (C_AXI_ADDR_WIDTH > 8)
+ ? (C_AXI_ADDR_WIDTH+1) : 9;
+
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ //
+ // Wires necessary for interacting with the formal property file
+ // {{{
+ // ...
+ // }}}
+
+ // Other registers to keep simplify keeping track of our progress
+ // {{{
+ reg [C_AXI_ADDR_WIDTH-1:0] faxi_rd_cknext, faxi_wr_cknext,
+ f_read_beat_addr, f_write_beat_addr,
+ faxi_read_beat_addr;
+ reg [C_AXI_ADDR_WIDTH-1:0] f_read_ckbeat_addr;
+ reg [FAXI_DEPTH-1:0] f_rd_bursts_after_check;
+
+ reg [C_AXI_ADDR_WIDTH:0] f_vfill;
+ reg [C_AXI_ADDR_WIDTH:0] f_space_available,
+ f_data_available;
+
+ reg [C_AXI_ADDR_WIDTH-1:0] f_read_checksum;
+ reg [C_AXI_ADDR_WIDTH:0] mem_space_available, mem_data_available;
+ // }}}
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The main AXI data interface bus property check
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // The number of beats in the maximum burst size is
+ // automatically determined from LGMAXBURST, and so its
+ // forced to be a power of two this way.
+ localparam MAXBURST=(1<<LGMAXBURST);
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_MAXBURST(MAXBURST-1),
+ .OPT_NARROW_BURST(1'b0),
+ .OPT_ASYNC_RESET(1'b0), // We don't use asynchronous resets
+ .OPT_EXCLUSIVE(1'b0), // We don't use the LOCK signal
+ .F_OPT_ASSUME_RESET(1'b1), // We aren't generating the reset
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(FAXI_DEPTH) // Width of the counters
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ // Write signals
+ // {{{
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .i_axi_awid( M_AXI_AWID),
+ .i_axi_awaddr( M_AXI_AWADDR),
+ .i_axi_awlen( M_AXI_AWLEN),
+ .i_axi_awsize( M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock( M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot( M_AXI_AWPROT),
+ .i_axi_awqos( M_AXI_AWQOS),
+ //
+ .i_axi_wvalid(M_AXI_WVALID),
+ .i_axi_wready(M_AXI_WREADY),
+ .i_axi_wdata( M_AXI_WDATA),
+ .i_axi_wstrb( M_AXI_WSTRB),
+ .i_axi_wlast( M_AXI_WLAST),
+ //
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid( M_AXI_BID),
+ .i_axi_bresp( M_AXI_BRESP),
+ // }}}
+ // Read signals
+ // {{{
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .i_axi_arid( M_AXI_ARID),
+ .i_axi_araddr( M_AXI_ARADDR),
+ .i_axi_arlen( M_AXI_ARLEN),
+ .i_axi_arsize( M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock( M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot( M_AXI_ARPROT),
+ .i_axi_arqos( M_AXI_ARQOS),
+ //
+ //
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rid( M_AXI_RID),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rlast( M_AXI_RLAST),
+ .i_axi_rresp( M_AXI_RRESP),
+ // }}}
+ // Induction signals
+ // {{{
+ // ...
+ // }}}
+ // }}}
+ );
+
+ // Some quick sanity checks
+ // {{{
+ always @(*)
+ begin
+ //
+ // ...
+ //
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write sanity checking
+ // {{{
+ always @(*)
+ mem_space_available = { mem_space_available_w,
+ {(LGMAXBURST+ADDRLSB){1'b0} } };
+
+ // Let's calculate the address of each write beat
+ // {{{
+ initial f_write_beat_addr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_write_beat_addr <= 0;
+ else if ((!M_AXI_WVALID || (M_AXI_WREADY && M_AXI_WLAST)) && soft_reset)
+ f_write_beat_addr <= 0;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ f_write_beat_addr <= f_write_beat_addr + (1<<ADDRLSB);
+ // }}}
+
+ //
+ // ...
+ //
+
+ // Verify during any write burst that all the burst parameters are
+ // correct
+ // {{{
+ // ...
+ // }}}
+
+ always @(*)
+ if (M_AXI_AWVALID)
+ begin
+ assert(writes_pending == (1<<LGMAXBURST));
+ // ...
+ end else
+ // ...
+
+ always @(*)
+ assert(M_AXI_WVALID == (writes_pending != 0));
+
+ //
+ // ...
+ //
+
+ // Check the writes-idle signal
+ // {{{
+ // ...
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read sanity checking
+ // {{{
+
+ always @(*)
+ mem_data_available = { mem_data_available_w,
+ {(LGMAXBURST+ADDRLSB){1'b0} } };
+ //
+ // ...
+ // Check the reads-idle signal
+ // {{{
+ // ...
+ // }}}
+
+ // Regenerate the read beat address
+ // {{{
+ // This works because we are using a single AXI ID for all of our reads.
+ // Therefore we can guarantee that reads will come back in order, thus
+ // we can count the return address here.
+ initial f_read_beat_addr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_read_beat_addr <= 0;
+ else if (soft_reset && reads_idle)
+ f_read_beat_addr <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ f_read_beat_addr <= f_read_beat_addr + (1<<ADDRLSB);
+ // }}}
+
+ // Read burst checking
+ // {{{
+
+ //
+ // ...
+ //
+
+ // }}}
+
+
+ // Match our read beat address to ARADDR
+ // {{{
+ // ...
+ // }}}
+
+ // Insist that our burst counters are consistent with bursts of a full
+ // length only
+ // {{{
+ // ...
+ // }}}
+
+ // RLAST checking
+ // {{{
+ // ...
+ // }}}
+
+ // Match the read beat address to the number of items remaining in this
+ // burst
+ // {{{
+ // ...
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal assertions (Induction)
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // On either error, or while waiting for a soft reset to complete
+ // nothing new should issue
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && ($past(soft_reset) || $past(o_err)))
+ begin
+ assert(!$rose(M_AXI_AWVALID));
+ assert(!$rose(M_AXI_WVALID));
+ assert(!$rose(M_AXI_ARVALID));
+ assert(!phantom_write);
+ assert(!phantom_read);
+ end
+ // }}}
+
+ //
+ // Writes and reads always have enough room
+
+ // Bus writes aren't allowed to drain the incoming FIFO dry
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!soft_reset && M_AXI_WVALID)
+ assert(ififo_fill + (skd_valid ? 1:0) >= writes_pending);
+ // }}}
+
+ // Bus reads aren't allowed to overflow the return FIFO
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!soft_reset && M_AXI_RVALID)
+ assert(!ofifo_full);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write checks
+ //
+
+ // Make sure the skid buffer only reads when either there's nothing
+ // held in the buffer, or the write channel has accepted data. Indeed,
+ // the write channel should never be active unless the skid buffer is
+ // full.
+ // {{{
+ always @(*)
+ if (!soft_reset && !skd_valid)
+ assert(!M_AXI_WVALID);
+
+ always @(*)
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ assert(read_from_fifo);
+ end else if (!skd_valid && !ififo_empty)
+ assert(read_from_fifo);
+ // }}}
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read checks
+ // {{{
+
+ // No read checks in addition to the ones above
+
+ // }}}
+
+ ////////////////////////////////////////
+ //
+ // Errors or resets -- do we properly end gracefully
+ //
+ // {{{
+ // Set o_err on any bus error. Only clear it on reset
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN))
+ begin
+ if ($past(M_AXI_BVALID && M_AXI_BRESP[1]))
+ assert(o_err);
+ if ($past(M_AXI_RVALID && M_AXI_RRESP[1]))
+ assert(o_err);
+ if ($past(!M_AXI_BVALID || !M_AXI_BRESP[1])
+ && $past(!M_AXI_RVALID || !M_AXI_RRESP[1]))
+ assert(!$rose(o_err));
+ end
+
+ // Only release soft_reset once the bus is idle
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN) && $fell(soft_reset))
+ begin
+ //
+ // ...
+ //
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+ end
+ // }}}
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FIFO checks
+ // {{{
+
+ // Check the global fill/emtpy values
+ // {{{
+ always @(*)
+ assert(vfifo_full == (vfifo_fill == (1<<(C_AXI_ADDR_WIDTH-ADDRLSB))));
+
+ always @(*)
+ assert(vfifo_fill <= (1<<(C_AXI_ADDR_WIDTH-ADDRLSB)));
+
+ always @(*)
+ assert(vfifo_empty == (vfifo_fill == 0));
+ // }}}
+
+ // An equation for vfill based upon our property checker's counters
+ // {{{
+ always @(*)
+ begin
+ f_vfill = M_AXI_AWADDR - M_AXI_ARADDR;
+ f_vfill[C_AXI_ADDR_WIDTH] = 0;
+ if (!M_AXI_AWVALID)
+ f_vfill = f_vfill - (writes_pending << ADDRLSB);
+ // ...
+ if (skd_valid)
+ f_vfill = f_vfill + (1<<ADDRLSB);
+ f_vfill = f_vfill + ((ififo_fill + ofifo_fill)<<ADDRLSB);
+ end
+ // }}}
+
+ // Make sure the virtual FIFO's fill matches that counter
+ // {{{
+ always @(*)
+ if (!soft_reset)
+ begin
+ assert(vfifo_fill == (f_vfill >> ADDRLSB));
+ assert(f_vfill <= (1<<(C_AXI_ADDR_WIDTH)));
+
+ // Before the equation check, double check that we
+ // don't overflow any of our arithmetic. Then check our
+ // virtual FIFO's fill counter against the various internal
+ // FIFO fills and read counters.
+
+ // These are just inequality checks, however, so we'll still
+ // need a full equation check elsewhere
+ //
+ // ...
+ //
+ end
+
+ // Make certain we aren't overflowing in our subtraction above
+ always @(*)
+ if (M_AXI_ARVALID && !soft_reset)
+ assert(M_AXI_ARADDR != M_AXI_AWADDR);
+ // }}}
+
+ // Check mem_space_available
+ // {{{
+ always @(*)
+ begin
+ f_space_available = (M_AXI_AWADDR - M_AXI_ARADDR);
+ f_space_available[C_AXI_ADDR_WIDTH] = 0;
+ f_space_available = (1<<C_AXI_ADDR_WIDTH) - f_space_available;
+ if (M_AXI_AWVALID && !phantom_write)
+ f_space_available = f_space_available
+ - (1 << (LGMAXBURST+ADDRLSB));
+ //
+ // ...
+ end
+
+ always @(*)
+ begin
+ assert({ mem_data_available_w, {(LGMAXBURST){1'b0}} }
+ <= vfifo_fill);
+ // ...
+ assert(mem_data_available <= (1<<C_AXI_ADDR_WIDTH));
+ assert(mem_space_available <= (1<<C_AXI_ADDR_WIDTH));
+ if (!soft_reset)
+ begin
+ assert(mem_space_available == f_space_available);
+
+ if (mem_space_available[C_AXI_ADDR_WIDTH])
+ begin
+ assert(M_AXI_ARADDR == M_AXI_AWADDR);
+ assert(!M_AXI_AWVALID || phantom_write);
+ // ...
+ end
+ end
+ end
+ // }}}
+
+ // Check mem_data_available
+ // {{{
+ // First, generate an equation to describe it
+ always @(*)
+ begin
+ f_data_available = M_AXI_AWADDR - M_AXI_ARADDR;
+ f_data_available[C_AXI_ADDR_WIDTH] = 0;
+ // ...
+ if (M_AXI_ARVALID && !phantom_read)
+ f_data_available = f_data_available
+ - (1 << (ADDRLSB + LGMAXBURST));
+ end
+
+ // Then compare it against that equation
+ always @(*)
+ if (!soft_reset)
+ begin
+ assert(mem_data_available == f_data_available);
+ if (mem_data_available[C_AXI_ADDR_WIDTH])
+ begin
+ assert(vfifo_fill[C_AXI_ADDR_WIDTH]);
+ assert(ofifo_empty);
+ end
+ end
+ // }}}
+
+ // ofifo_space_available
+ // {{{
+ always @(*)
+ if (!reset_fifo)
+ begin
+ // ...
+
+ // Make sure we don't overflow above
+ assert(ofifo_space_available <= (1<<LGFIFO));
+ end
+ // }}}
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Initial (only) constraints
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+
+ assert(mem_space_available == (1<<C_AXI_ADDR_WIDTH));
+ assert(mem_data_available == 0);
+
+ assert(!phantom_read);
+ assert(!phantom_write);
+
+ assert(vfifo_fill == 0);
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // This core doesn't (yet) have any formal contract check
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [2:0] cvr_write_bursts, cvr_read_bursts;
+ (* anyconst *) reg cvr_high_speed;
+
+ always @(posedge S_AXI_ACLK)
+ if (cvr_high_speed)
+ begin
+ assume(!$fell(S_AXIS_TVALID));
+ // if (S_AXI_ARESETN && $past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ // assume(S_AXIS_TVALID && $stable(S_AXIS_TDATA));
+
+ assume(M_AXI_AWREADY || writes_pending > 0);
+ assume(M_AXIS_TREADY);
+ assume(M_AXI_WREADY);
+ assume(M_AXI_ARREADY);
+ end
+
+ initial cvr_write_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset || o_err || o_overflow)
+ cvr_write_bursts <= 0;
+ else if (M_AXI_BVALID && !cvr_write_bursts[2])
+ cvr_write_bursts <= cvr_write_bursts + 1;
+
+ initial cvr_read_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset || o_err || o_overflow)
+ cvr_read_bursts <= 0;
+ else if (M_AXI_RVALID && M_AXI_RLAST && !cvr_read_bursts[2])
+ cvr_read_bursts <= cvr_read_bursts + 1;
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !soft_reset)
+ cover(cvr_read_bursts > 1 && cvr_write_bursts > 1);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !soft_reset)
+ cover(cvr_read_bursts > 1 && cvr_write_bursts > 1
+ && cvr_high_speed);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions (i.e. constraints)
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // No careless assumptions. Indeed, no assumptions at all beyond
+ // what's in the bus property file, and some optional cover assumptions.
+
+ // }}}
+ // }}}
+`endif
+endmodule
diff --git a/rtl/wb2axip/axixbar.v b/rtl/wb2axip/axixbar.v
new file mode 100644
index 0000000..f2e1ac6
--- /dev/null
+++ b/rtl/wb2axip/axixbar.v
@@ -0,0 +1,2585 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axixbar.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Create a full crossbar between NM AXI sources (masters), and NS
+// AXI slaves. Every master can talk to any slave, provided it
+// isn't already busy.
+// {{{
+// Performance: This core has been designed with the goal of being able to push
+// one transaction through the interconnect, from any master to
+// any slave, per clock cycle. This may perhaps be its most unique
+// feature. While throughput is good, latency is something else.
+//
+// The arbiter requires a clock to switch, then another clock to send data
+// downstream. This creates a minimum two clock latency up front. The
+// return path suffers another clock of latency as well, placing the
+// minimum latency at four clocks. The minimum write latency is at
+// least one clock longer, since the write data must wait for the write
+// address before proceeeding.
+//
+// Note that this arbiter only forwards AxID fields. It does not use
+// them in arbitration. As a result, only one master may ever make
+// requests of any given slave at a time. All responses from a slave
+// will be returned to that known master. This is a known limitation in
+// this implementation which will be fixed (in time) with funding and
+// interest. Until that time, in order for a second master to access
+// a given slave, the first master must receive all of its acknowledgments.
+//
+// Usage: To use, you must first set NM and NS to the number of masters
+// and the number of slaves you wish to connect to. You then need to
+// adjust the addresses of the slaves, found SLAVE_ADDR array. Those
+// bits that are relevant in SLAVE_ADDR to then also be set in SLAVE_MASK.
+// Adjusting the data and address widths go without saying.
+//
+// Lower numbered masters are given priority in any "fight".
+//
+// Channel grants are given on the condition that 1) they are requested,
+// 2) no other channel has a grant, 3) all of the responses have been
+// received from the current channel, and 4) the internal counters are
+// not overflowing.
+//
+// The core limits the number of outstanding transactions on any channel to
+// 1<<LGMAXBURST-1.
+//
+// Channel grants are lost 1) after OPT_LINGER clocks of being idle, or
+// 2) when another master requests an idle (but still lingering) channel
+// assignment, or 3) once all the responses have been returned to the
+// current channel, and the current master is requesting another channel.
+//
+// A special slave is allocated for the case of no valid address.
+//
+// Since the write channel has no address information, the write data
+// channel always be delayed by at least one clock from the write address
+// channel.
+//
+// If OPT_LOWPOWER is set, then unused values will be set to zero.
+// This can also be used to help identify relevant values within any
+// trace.
+//
+// Known issues: This module can be a challenge to wire up.
+//
+// In order to keep the build lint clean, it's important that every
+// port be connected. In order to be flexible regarding the number of
+// ports that can be connected, the various AXI signals, whether input
+// or output, have been concatenated together across either all masters
+// or all slaves. This can make the design a lesson in tediousness to
+// wire up.
+//
+// I commonly wire this crossbar up using AutoFPGA--just to make certain
+// that I do it right and don't make mistakes when wiring it up. This
+// also handles the tediousness involved.
+//
+// I have also done this by hand.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+// }}}
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module axixbar #(
+ // {{{
+ parameter integer C_AXI_DATA_WIDTH = 32,
+ parameter integer C_AXI_ADDR_WIDTH = 32,
+ parameter integer C_AXI_ID_WIDTH = 2,
+ //
+ // NM is the number of masters driving incoming slave channels
+ parameter NM = 4,
+ //
+ // NS is the number of slaves connected to the crossbar, driven
+ // by the master channels output from this IP.
+ parameter NS = 8,
+ //
+ // SLAVE_ADDR is an array of addresses, describing each of
+ // {{{
+ // the slave channels. It works tightly with SLAVE_MASK,
+ // so that when (ADDR & MASK == ADDR), the channel in question
+ // has been requested.
+ //
+ // It is an internal in the setup of this core to doubly map
+ // an address, such that (addr & SLAVE_MASK[k])==SLAVE_ADDR[k]
+ // for two separate values of k.
+ //
+ // Any attempt to access an address that is a hole in this
+ // address list will result in a returned xRESP value of
+ // INTERCONNECT_ERROR (2'b11)
+ //
+ // NOTE: This is only a nominal address set. I expect that
+ // any design using the crossbar will need to adjust both
+ // SLAVE_ADDR and SLAVE_MASK, if not also NM and NS.
+ parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_ADDR = {
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b110, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b101, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b100, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b011, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b010, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 4'b0001, {(C_AXI_ADDR_WIDTH-4){1'b0}},
+ 4'b0000, {(C_AXI_ADDR_WIDTH-4){1'b0}} },
+ // }}}
+ //
+ // SLAVE_MASK: is an array, much like SLAVE_ADDR, describing
+ // {{{
+ // which of the bits in SLAVE_ADDR are relevant. It is
+ // important to maintain for every slave that
+ // (~SLAVE_MASK[i] & SLAVE_ADDR[i]) == 0.
+ //
+ // NOTE: This value should be overridden by any implementation.
+ // Verilator lint_off WIDTH
+ parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_MASK = {
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}},
+ 4'b1111, {(C_AXI_ADDR_WIDTH-4){1'b0}},
+ 4'b1111, {(C_AXI_ADDR_WIDTH-4){1'b0}} },
+ // Verilator lint_on WIDTH
+ // }}}
+ //
+ // OPT_LOWPOWER: If set, it forces all unused values to zero,
+ // {{{
+ // preventing them from unnecessarily toggling. This will
+ // raise the logic count of the core, but might also lower
+ // the power used by the interconnect and the bus driven wires
+ // which (in my experience) tend to have a high fan out.
+ parameter [0:0] OPT_LOWPOWER = 0,
+ // }}}
+ //
+ // OPT_LINGER: Set this to the number of clocks an idle
+ // {{{
+ // channel shall be left open before being closed. Once
+ // closed, it will take a minimum of two clocks before the
+ // channel can be opened and data transmitted through it again.
+ parameter OPT_LINGER = 4,
+ // }}}
+ //
+ // [EXPERIMENTAL] OPT_QOS: If set, the QOS transmission values
+ // {{{
+ // will be honored when determining who wins arbitration for
+ // accessing a given slave. (This feature has not yet been
+ // verified)
+ parameter [0:0] OPT_QOS = 0,
+ // }}}
+ //
+ // LGMAXBURST: Specifies the log based two of the maximum
+ // {{{
+ // number of bursts transactions that may be outstanding at any
+ // given time. This is different from the maximum number of
+ // outstanding beats.
+ parameter LGMAXBURST = 3
+ // }}}
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // Write slave channels from the controlling AXI masters
+ // {{{
+ input wire [NM-1:0] S_AXI_AWVALID,
+ output wire [NM-1:0] S_AXI_AWREADY,
+ input wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
+ input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [NM*8-1:0] S_AXI_AWLEN,
+ input wire [NM*3-1:0] S_AXI_AWSIZE,
+ input wire [NM*2-1:0] S_AXI_AWBURST,
+ // Verilator coverage_off
+ input wire [NM-1:0] S_AXI_AWLOCK,
+ input wire [NM*4-1:0] S_AXI_AWCACHE,
+ input wire [NM*3-1:0] S_AXI_AWPROT,
+ input wire [NM*4-1:0] S_AXI_AWQOS,
+ // Verilator coverage_on
+ //
+ input wire [NM-1:0] S_AXI_WVALID,
+ output wire [NM-1:0] S_AXI_WREADY,
+ input wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [NM*C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ input wire [NM-1:0] S_AXI_WLAST,
+ //
+ output wire [NM-1:0] S_AXI_BVALID,
+ input wire [NM-1:0] S_AXI_BREADY,
+ output wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_BID,
+ output wire [NM*2-1:0] S_AXI_BRESP,
+ // }}}
+ // Read slave channels from the controlling AXI masters
+ // {{{
+ input wire [NM-1:0] S_AXI_ARVALID,
+ output wire [NM-1:0] S_AXI_ARREADY,
+ input wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
+ input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [NM*8-1:0] S_AXI_ARLEN,
+ input wire [NM*3-1:0] S_AXI_ARSIZE,
+ input wire [NM*2-1:0] S_AXI_ARBURST,
+ // Verilator coverage_off
+ input wire [NM-1:0] S_AXI_ARLOCK,
+ input wire [NM*4-1:0] S_AXI_ARCACHE,
+ input wire [NM*3-1:0] S_AXI_ARPROT,
+ input wire [NM*4-1:0] S_AXI_ARQOS,
+ // Verilator coverage_on
+ //
+ output wire [NM-1:0] S_AXI_RVALID,
+ input wire [NM-1:0] S_AXI_RREADY,
+ output wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_RID,
+ output wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [NM*2-1:0] S_AXI_RRESP,
+ output wire [NM-1:0] S_AXI_RLAST,
+ // }}}
+ // Write channel master outputs to the connected AXI slaves
+ // {{{
+ output wire [NS-1:0] M_AXI_AWVALID,
+ input wire [NS-1:0] M_AXI_AWREADY,
+ output wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+ output wire [NS*8-1:0] M_AXI_AWLEN,
+ output wire [NS*3-1:0] M_AXI_AWSIZE,
+ output wire [NS*2-1:0] M_AXI_AWBURST,
+ // Verilator coverage_off
+ output wire [NS-1:0] M_AXI_AWLOCK,
+ output wire [NS*4-1:0] M_AXI_AWCACHE,
+ output wire [NS*3-1:0] M_AXI_AWPROT,
+ output wire [NS*4-1:0] M_AXI_AWQOS,
+ // Verilator coverage_on
+ //
+ //
+ output wire [NS-1:0] M_AXI_WVALID,
+ input wire [NS-1:0] M_AXI_WREADY,
+ output wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [NS*C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire [NS-1:0] M_AXI_WLAST,
+ //
+ input wire [NS-1:0] M_AXI_BVALID,
+ output wire [NS-1:0] M_AXI_BREADY,
+ input wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [NS*2-1:0] M_AXI_BRESP,
+ // }}}
+ // Read channel master outputs to the connected AXI slaves
+ // {{{
+ output wire [NS-1:0] M_AXI_ARVALID,
+ input wire [NS-1:0] M_AXI_ARREADY,
+ output wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+ output wire [NS*8-1:0] M_AXI_ARLEN,
+ output wire [NS*3-1:0] M_AXI_ARSIZE,
+ output wire [NS*2-1:0] M_AXI_ARBURST,
+ // Verilator coverage_off
+ output wire [NS-1:0] M_AXI_ARLOCK,
+ output wire [NS*4-1:0] M_AXI_ARCACHE,
+ output wire [NS*4-1:0] M_AXI_ARQOS,
+ output wire [NS*3-1:0] M_AXI_ARPROT,
+ // Verilator coverage_on
+ //
+ //
+ input wire [NS-1:0] M_AXI_RVALID,
+ output wire [NS-1:0] M_AXI_RREADY,
+ input wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire [NS*2-1:0] M_AXI_RRESP,
+ input wire [NS-1:0] M_AXI_RLAST
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal signal declarations and definitions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Local parameters, derived from those above
+ // {{{
+ // IW, AW, and DW, are short-hand abbreviations used locally.
+ localparam IW = C_AXI_ID_WIDTH;
+ localparam AW = C_AXI_ADDR_WIDTH;
+ localparam DW = C_AXI_DATA_WIDTH;
+ // LGLINGER tells us how many bits we need for counting how long
+ // to keep an udle channel open.
+ localparam LGLINGER = (OPT_LINGER>1) ? $clog2(OPT_LINGER+1) : 1;
+ //
+ localparam LGNM = (NM>1) ? $clog2(NM) : 1;
+ localparam LGNS = (NS>1) ? $clog2(NS+1) : 1;
+ //
+ // In order to use indexes, and hence fully balanced mux trees, it helps
+ // to make certain that we have a power of two based lookup. NMFULL
+ // is the number of masters in this lookup, with potentially some
+ // unused extra ones. NSFULL is defined similarly.
+ localparam NMFULL = (NM>1) ? (1<<LGNM) : 1;
+ localparam NSFULL = (NS>1) ? (1<<LGNS) : 2;
+ //
+ localparam [1:0] INTERCONNECT_ERROR = 2'b11;
+ //
+ // OPT_SKID_INPUT controls whether the input skid buffers register
+ // their outputs or not. If set, all skid buffers will cost one more
+ // clock of latency. It's not clear that there's a performance gain
+ // to be had by setting this.
+ localparam [0:0] OPT_SKID_INPUT = 0;
+ //
+ // OPT_BUFFER_DECODER determines whether or not the outputs of the
+ // address decoder will be buffered or not. If buffered, there will
+ // be an extra (registered) clock delay on each of the A* channels from
+ // VALID to issue.
+ localparam [0:0] OPT_BUFFER_DECODER = 1;
+ //
+ // OPT_AWW controls whether or not a W* beat may be issued to a slave
+ // at the same time as the first AW* beat gets sent to the slave. Set
+ // to 1'b1 for lower latency, at the potential cost of a greater
+ // combinatorial path length
+ localparam OPT_AWW = 1'b1;
+ // }}}
+
+ genvar N,M;
+ integer iN, iM;
+
+ reg [NSFULL-1:0] wrequest [0:NM-1];
+ reg [NSFULL-1:0] rrequest [0:NM-1];
+ reg [NSFULL-1:0] wrequested [0:NM];
+ reg [NSFULL-1:0] rrequested [0:NM];
+ reg [NS:0] wgrant [0:NM-1];
+ reg [NS:0] rgrant [0:NM-1];
+ reg [NM-1:0] mwgrant;
+ reg [NM-1:0] mrgrant;
+ reg [NS-1:0] swgrant;
+ reg [NS-1:0] srgrant;
+
+ // verilator lint_off UNUSED
+ wire [LGMAXBURST-1:0] w_mawpending [0:NM-1];
+ wire [LGMAXBURST-1:0] wlasts_pending [0:NM-1];
+ wire [LGMAXBURST-1:0] w_mrpending [0:NM-1];
+ // verilator lint_on UNUSED
+ reg [NM-1:0] mwfull;
+ reg [NM-1:0] mrfull;
+ reg [NM-1:0] mwempty;
+ reg [NM-1:0] mrempty;
+ //
+ wire [LGNS-1:0] mwindex [0:NMFULL-1];
+ wire [LGNS-1:0] mrindex [0:NMFULL-1];
+ wire [LGNM-1:0] swindex [0:NSFULL-1];
+ wire [LGNM-1:0] srindex [0:NSFULL-1];
+
+ wire [NM-1:0] wdata_expected;
+
+ // The shadow buffers
+ wire [NMFULL-1:0] m_awvalid, m_arvalid;
+ wire [NMFULL-1:0] m_wvalid;
+ wire [NM-1:0] dcd_awvalid, dcd_arvalid;
+
+ wire [C_AXI_ID_WIDTH-1:0] m_awid [0:NMFULL-1];
+ wire [C_AXI_ADDR_WIDTH-1:0] m_awaddr [0:NMFULL-1];
+ wire [7:0] m_awlen [0:NMFULL-1];
+ wire [2:0] m_awsize [0:NMFULL-1];
+ wire [1:0] m_awburst [0:NMFULL-1];
+ wire [NMFULL-1:0] m_awlock;
+ wire [3:0] m_awcache [0:NMFULL-1];
+ wire [2:0] m_awprot [0:NMFULL-1];
+ wire [3:0] m_awqos [0:NMFULL-1];
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] m_wdata [0:NMFULL-1];
+ wire [C_AXI_DATA_WIDTH/8-1:0] m_wstrb [0:NMFULL-1];
+ wire [NMFULL-1:0] m_wlast;
+
+ wire [C_AXI_ID_WIDTH-1:0] m_arid [0:NMFULL-1];
+ wire [C_AXI_ADDR_WIDTH-1:0] m_araddr [0:NMFULL-1];
+ wire [8-1:0] m_arlen [0:NMFULL-1];
+ wire [3-1:0] m_arsize [0:NMFULL-1];
+ wire [2-1:0] m_arburst [0:NMFULL-1];
+ wire [NMFULL-1:0] m_arlock;
+ wire [4-1:0] m_arcache [0:NMFULL-1];
+ wire [2:0] m_arprot [0:NMFULL-1];
+ wire [3:0] m_arqos [0:NMFULL-1];
+ //
+ //
+ reg [NM-1:0] berr_valid;
+ reg [IW-1:0] berr_id [0:NM-1];
+ //
+ reg [NM-1:0] rerr_none;
+ reg [NM-1:0] rerr_last;
+ reg [8:0] rerr_outstanding [0:NM-1];
+ reg [IW-1:0] rerr_id [0:NM-1];
+
+ wire [NM-1:0] skd_awvalid, skd_awstall;
+ wire [NM-1:0] skd_arvalid, skd_arstall;
+ wire [IW-1:0] skd_awid [0:NM-1];
+ wire [AW-1:0] skd_awaddr [0:NM-1];
+ wire [8-1:0] skd_awlen [0:NM-1];
+ wire [3-1:0] skd_awsize [0:NM-1];
+ wire [2-1:0] skd_awburst [0:NM-1];
+ wire [NM-1:0] skd_awlock;
+ wire [4-1:0] skd_awcache [0:NM-1];
+ wire [3-1:0] skd_awprot [0:NM-1];
+ wire [4-1:0] skd_awqos [0:NM-1];
+ //
+ wire [IW-1:0] skd_arid [0:NM-1];
+ wire [AW-1:0] skd_araddr [0:NM-1];
+ wire [8-1:0] skd_arlen [0:NM-1];
+ wire [3-1:0] skd_arsize [0:NM-1];
+ wire [2-1:0] skd_arburst [0:NM-1];
+ wire [NM-1:0] skd_arlock;
+ wire [4-1:0] skd_arcache [0:NM-1];
+ wire [3-1:0] skd_arprot [0:NM-1];
+ wire [4-1:0] skd_arqos [0:NM-1];
+
+ // Verilator lint_off UNUSED
+ reg [NSFULL-1:0] m_axi_awvalid;
+ reg [NSFULL-1:0] m_axi_awready;
+ reg [IW-1:0] m_axi_awid [0:NSFULL-1];
+ reg [7:0] m_axi_awlen [0:NSFULL-1];
+
+ reg [NSFULL-1:0] m_axi_wvalid;
+ reg [NSFULL-1:0] m_axi_wready;
+ reg [NSFULL-1:0] m_axi_bvalid;
+ reg [NSFULL-1:0] m_axi_bready;
+ // Verilator lint_on UNUSED
+ reg [1:0] m_axi_bresp [0:NSFULL-1];
+ reg [IW-1:0] m_axi_bid [0:NSFULL-1];
+
+ // Verilator lint_off UNUSED
+ reg [NSFULL-1:0] m_axi_arvalid;
+ reg [7:0] m_axi_arlen [0:NSFULL-1];
+ reg [IW-1:0] m_axi_arid [0:NSFULL-1];
+ reg [NSFULL-1:0] m_axi_arready;
+ // Verilator lint_on UNUSED
+ reg [NSFULL-1:0] m_axi_rvalid;
+ // Verilator lint_off UNUSED
+ reg [NSFULL-1:0] m_axi_rready;
+ // Verilator lint_on UNUSED
+ //
+ reg [IW-1:0] m_axi_rid [0:NSFULL-1];
+ reg [DW-1:0] m_axi_rdata [0:NSFULL-1];
+ reg [NSFULL-1:0] m_axi_rlast;
+ reg [2-1:0] m_axi_rresp [0:NSFULL-1];
+
+ reg [NM-1:0] slave_awaccepts;
+ reg [NM-1:0] slave_waccepts;
+ reg [NM-1:0] slave_raccepts;
+
+ reg [NM-1:0] bskd_valid;
+ reg [NM-1:0] rskd_valid, rskd_rlast;
+ wire [NM-1:0] bskd_ready;
+ wire [NM-1:0] rskd_ready;
+
+ wire [NMFULL-1:0] write_qos_lockout,
+ read_qos_lockout;
+
+ reg [NSFULL-1:0] slave_awready, slave_wready, slave_arready;
+
+ // m_axi_* convenience signals (write side)
+ // {{{
+ always @(*)
+ begin
+ m_axi_awvalid = -1;
+ m_axi_awready = -1;
+ m_axi_wvalid = -1;
+ m_axi_wready = -1;
+ m_axi_bvalid = 0;
+ m_axi_bready = -1;
+
+ m_axi_awvalid[NS-1:0] = M_AXI_AWVALID;
+ m_axi_awready[NS-1:0] = M_AXI_AWREADY;
+ m_axi_wvalid[NS-1:0] = M_AXI_WVALID;
+ m_axi_wready[NS-1:0] = M_AXI_WREADY;
+ m_axi_bvalid[NS-1:0] = M_AXI_BVALID;
+ m_axi_bready[NS-1:0] = M_AXI_BREADY;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ m_axi_awid[iM] = M_AXI_AWID[ iM*IW +: IW];
+ m_axi_awlen[iM] = M_AXI_AWLEN[ iM* 8 +: 8];
+
+ m_axi_bid[iM] = M_AXI_BID[iM* IW +: IW];
+ m_axi_bresp[iM] = M_AXI_BRESP[iM* 2 +: 2];
+
+ m_axi_rid[iM] = M_AXI_RID[ iM*IW +: IW];
+ m_axi_rdata[iM] = M_AXI_RDATA[iM*DW +: DW];
+ m_axi_rresp[iM] = M_AXI_RRESP[iM* 2 +: 2];
+ m_axi_rlast[iM] = M_AXI_RLAST[iM];
+ end
+ for(iM=NS; iM<NSFULL; iM=iM+1)
+ begin
+ m_axi_awid[iM] = 0;
+ m_axi_awlen[iM] = 0;
+
+ m_axi_bresp[iM] = INTERCONNECT_ERROR;
+ m_axi_bid[iM] = 0;
+
+ m_axi_rid[iM] = 0;
+ m_axi_rdata[iM] = 0;
+ m_axi_rresp[iM] = INTERCONNECT_ERROR;
+ m_axi_rlast[iM] = 1;
+ end
+ end
+ // }}}
+
+ // m_axi_* convenience signals (read side)
+ // {{{
+ always @(*)
+ begin
+ m_axi_arvalid = 0;
+ m_axi_arready = 0;
+ m_axi_rvalid = 0;
+ m_axi_rready = 0;
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ m_axi_arlen[iM] = M_AXI_ARLEN[iM* 8 +: 8];
+ m_axi_arid[iM] = M_AXI_ARID[ iM*IW +: IW];
+ end
+ for(iM=NS; iM<NSFULL; iM=iM+1)
+ begin
+ m_axi_arlen[iM] = 0;
+ m_axi_arid[iM] = 0;
+ end
+
+ m_axi_arvalid[NS-1:0] = M_AXI_ARVALID;
+ m_axi_arready[NS-1:0] = M_AXI_ARREADY;
+ m_axi_rvalid[NS-1:0] = M_AXI_RVALID;
+ m_axi_rready[NS-1:0] = M_AXI_RREADY;
+ end
+ // }}}
+
+ // slave_*ready convenience signals
+ // {{{
+ always @(*)
+ begin
+ // These are designed to keep us from doing things like
+ // m_axi_*[m?index[N]] && m_axi_*[m?index[N]] && .. etc
+ //
+ // First, we'll set bits for all slaves--to include those that
+ // are undefined (but required by our static analysis tools).
+ slave_awready = -1;
+ slave_wready = -1;
+ slave_arready = -1;
+ //
+ // Here we do all of the combinatoric calculations, so the
+ // master only needs to reference one bit of this signal
+ slave_awready[NS-1:0] = (~M_AXI_AWVALID | M_AXI_AWREADY);
+ slave_wready[NS-1:0] = (~M_AXI_WVALID | M_AXI_WREADY);
+ slave_arready[NS-1:0] = (~M_AXI_ARVALID | M_AXI_ARREADY);
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Process our incoming signals: AW*, W*, and AR*
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : W1_DECODE_WRITE_REQUEST
+ // {{{
+ wire [NS:0] wdecode;
+
+ // awskid, the skidbuffer for the incoming AW* channel
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2+1+4+3+4),
+ .OPT_OUTREG(OPT_SKID_INPUT)
+ // }}}
+ ) awskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID[N]), .o_ready(S_AXI_AWREADY[N]),
+ .i_data(
+ { S_AXI_AWID[N*IW +: IW], S_AXI_AWADDR[N*AW +: AW],
+ S_AXI_AWLEN[N*8 +: 8], S_AXI_AWSIZE[N*3 +: 3],
+ S_AXI_AWBURST[N*2 +: 2], S_AXI_AWLOCK[N],
+ S_AXI_AWCACHE[N*4 +: 4], S_AXI_AWPROT[N*3 +: 3],
+ S_AXI_AWQOS[N*4 +: 4] }),
+ .o_valid(skd_awvalid[N]), .i_ready(!skd_awstall[N]),
+ .o_data(
+ { skd_awid[N], skd_awaddr[N], skd_awlen[N],
+ skd_awsize[N], skd_awburst[N], skd_awlock[N],
+ skd_awcache[N], skd_awprot[N], skd_awqos[N] })
+ // }}}
+ );
+ // }}}
+
+ // wraddr, decode the write channel's address request to a
+ // particular slave index
+ // {{{
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(IW+8+3+2+1+4+3+4), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(OPT_BUFFER_DECODER)
+ // }}}
+ ) wraddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(skd_awvalid[N]), .o_stall(skd_awstall[N]),
+ .i_addr(skd_awaddr[N]), .i_data({ skd_awid[N],
+ skd_awlen[N], skd_awsize[N], skd_awburst[N],
+ skd_awlock[N], skd_awcache[N], skd_awprot[N],
+ skd_awqos[N] }),
+ .o_valid(dcd_awvalid[N]),
+ .i_stall(!dcd_awvalid[N]||!slave_awaccepts[N]),
+ .o_decode(wdecode), .o_addr(m_awaddr[N]),
+ .o_data({ m_awid[N], m_awlen[N], m_awsize[N],
+ m_awburst[N], m_awlock[N], m_awcache[N],
+ m_awprot[N], m_awqos[N]})
+ // }}}
+ );
+ // }}}
+
+ // wskid, the skid buffer for the incoming W* channel
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(DW+DW/8+1),
+ .OPT_OUTREG(OPT_SKID_INPUT || OPT_BUFFER_DECODER)
+ // }}}
+ ) wskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_WVALID[N]), .o_ready(S_AXI_WREADY[N]),
+ .i_data(
+ { S_AXI_WDATA[N*DW +: DW], S_AXI_WSTRB[N*DW/8 +: DW/8],
+ S_AXI_WLAST[N] }),
+ .o_valid(m_wvalid[N]), .i_ready(slave_waccepts[N]),
+ .o_data({ m_wdata[N], m_wstrb[N], m_wlast[N] })
+ // }}}
+ );
+ // }}}
+
+ // slave_awaccepts
+ // {{{
+ always @(*)
+ begin
+ slave_awaccepts[N] = 1'b1;
+
+ // Cannot accept/forward a packet without a bus grant
+ // This handles whether or not write data is still
+ // pending.
+ if (!mwgrant[N])
+ slave_awaccepts[N] = 1'b0;
+ if (write_qos_lockout[N])
+ slave_awaccepts[N] = 1'b0;
+ if (mwfull[N])
+ slave_awaccepts[N] = 1'b0;
+ // Don't accept a packet unless its to the same slave
+ // the grant is issued for
+ if (!wrequest[N][mwindex[N]])
+ slave_awaccepts[N] = 1'b0;
+ if (!wgrant[N][NS])
+ begin
+ if (!slave_awready[mwindex[N]])
+ slave_awaccepts[N] = 1'b0;
+ end else if (berr_valid[N] && !bskd_ready[N])
+ begin
+ // Can't accept an write address channel request
+ // for the no-address-mapped channel if the
+ // B* channel is stalled, lest we lose the ID
+ // of the transaction
+ //
+ // !berr_valid[N] => we have to accept more
+ // write data before we can issue BVALID
+ slave_awaccepts[N] = 1'b0;
+ end
+ end
+ // }}}
+
+ // slave_waccepts
+ // {{{
+ always @(*)
+ begin
+ slave_waccepts[N] = 1'b1;
+ if (!mwgrant[N])
+ slave_waccepts[N] = 1'b0;
+ if (!wdata_expected[N] && (!OPT_AWW || !slave_awaccepts[N]))
+ slave_waccepts[N] = 1'b0;
+ if (!wgrant[N][NS])
+ begin
+ if (!slave_wready[mwindex[N]])
+ slave_waccepts[N] = 1'b0;
+ end else if (berr_valid[N] && !bskd_ready[N])
+ slave_waccepts[N] = 1'b0;
+ end
+ // }}}
+
+ reg r_awvalid;
+
+ always @(*)
+ begin
+ r_awvalid = dcd_awvalid[N] && !mwfull[N];
+ wrequest[N]= 0;
+ if (!mwfull[N])
+ wrequest[N][NS:0] = wdecode;
+ end
+
+ assign m_awvalid[N] = r_awvalid;
+
+ // QOS handling via write_qos_lockout
+ // {{{
+ if (!OPT_QOS || NM == 1)
+ begin : WRITE_NO_QOS
+
+ // If we aren't using QOS, then never lock any packets
+ // out from arbitration
+ assign write_qos_lockout[N] = 0;
+
+ end else begin : WRITE_QOS
+
+ // Lock out a master based upon a second master having
+ // a higher QOS request level
+ // {{{
+ reg r_write_qos_lockout;
+
+ initial r_write_qos_lockout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_write_qos_lockout <= 0;
+ else begin
+ r_write_qos_lockout <= 0;
+
+ for(iN=0; iN<NM; iN=iN+1)
+ if (iN != N)
+ begin
+ if (m_awvalid[N]
+ &&(|(wrequest[iN][NS-1:0]
+ & wdecode[NS-1:0]))
+ &&(m_awqos[N] < m_awqos[iN]))
+ r_write_qos_lockout <= 1;
+ end
+ end
+
+ assign write_qos_lockout[N] = r_write_qos_lockout;
+ // }}}
+ end
+ // }}}
+
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_WSKID_BUFFERS
+ // {{{
+ // The following values are unused. They need to be defined
+ // so that our indexing scheme will work, but indexes should
+ // never actually reference them
+ assign m_awid[N] = 0;
+ assign m_awaddr[N] = 0;
+ assign m_awlen[N] = 0;
+ assign m_awsize[N] = 0;
+ assign m_awburst[N] = 0;
+ assign m_awlock[N] = 0;
+ assign m_awcache[N] = 0;
+ assign m_awprot[N] = 0;
+ assign m_awqos[N] = 0;
+
+ assign m_awvalid[N] = 0;
+
+ assign m_wvalid[N] = 0;
+ //
+ assign m_wdata[N] = 0;
+ assign m_wstrb[N] = 0;
+ assign m_wlast[N] = 0;
+
+ assign write_qos_lockout[N] = 0;
+ // }}}
+ // }}}
+ end endgenerate
+
+ // Read skid buffers and address decoding, slave_araccepts logic
+ generate for(N=0; N<NM; N=N+1)
+ begin : R1_DECODE_READ_REQUEST
+ // {{{
+ reg r_arvalid;
+ wire [NS:0] rdecode;
+
+ // arskid
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+AW+8+3+2+1+4+3+4),
+ .OPT_OUTREG(OPT_SKID_INPUT)
+ // }}}
+ ) arskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_ARVALID[N]), .o_ready(S_AXI_ARREADY[N]),
+ .i_data(
+ { S_AXI_ARID[N*IW +: IW], S_AXI_ARADDR[N*AW +: AW],
+ S_AXI_ARLEN[N*8 +: 8], S_AXI_ARSIZE[N*3 +: 3],
+ S_AXI_ARBURST[N*2 +: 2], S_AXI_ARLOCK[N],
+ S_AXI_ARCACHE[N*4 +: 4], S_AXI_ARPROT[N*3 +: 3],
+ S_AXI_ARQOS[N*4 +: 4] }),
+ .o_valid(skd_arvalid[N]), .i_ready(!skd_arstall[N]),
+ .o_data(
+ { skd_arid[N], skd_araddr[N], skd_arlen[N],
+ skd_arsize[N], skd_arburst[N], skd_arlock[N],
+ skd_arcache[N], skd_arprot[N], skd_arqos[N] })
+ // }}}
+ );
+ // }}}
+
+ // Read address decoder
+ // {{{
+ addrdecode #(
+ // {{{
+ .AW(AW), .DW(IW+8+3+2+1+4+3+4), .NS(NS),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(OPT_BUFFER_DECODER)
+ // }}}
+ ) rdaddr(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(skd_arvalid[N]), .o_stall(skd_arstall[N]),
+ .i_addr(skd_araddr[N]), .i_data({ skd_arid[N],
+ skd_arlen[N], skd_arsize[N], skd_arburst[N],
+ skd_arlock[N], skd_arcache[N], skd_arprot[N],
+ skd_arqos[N] }),
+ .o_valid(dcd_arvalid[N]),
+ .i_stall(!m_arvalid[N] || !slave_raccepts[N]),
+ .o_decode(rdecode), .o_addr(m_araddr[N]),
+ .o_data({ m_arid[N], m_arlen[N], m_arsize[N],
+ m_arburst[N], m_arlock[N], m_arcache[N],
+ m_arprot[N], m_arqos[N]})
+ // }}}
+ );
+ // }}}
+
+ always @(*)
+ begin
+ r_arvalid = dcd_arvalid[N] && !mrfull[N];
+ rrequest[N] = 0;
+ if (!mrfull[N])
+ rrequest[N][NS:0] = rdecode;
+ end
+
+ assign m_arvalid[N] = r_arvalid;
+
+ // slave_raccepts decoding
+ // {{{
+ always @(*)
+ begin
+ slave_raccepts[N] = 1'b1;
+ if (!mrgrant[N])
+ slave_raccepts[N] = 1'b0;
+ if (read_qos_lockout[N])
+ slave_raccepts[N] = 1'b0;
+ if (mrfull[N])
+ slave_raccepts[N] = 1'b0;
+ // If we aren't requesting access to the channel we've
+ // been granted access to, then we can't accept this
+ // verilator lint_off WIDTH
+ if (!rrequest[N][mrindex[N]])
+ slave_raccepts[N] = 1'b0;
+ // verilator lint_on WIDTH
+ if (!rgrant[N][NS])
+ begin
+ if (!slave_arready[mrindex[N]])
+ slave_raccepts[N] = 1'b0;
+ end else if (!mrempty[N] || !rerr_none[N] || rskd_valid[N])
+ slave_raccepts[N] = 1'b0;
+ end
+ // }}}
+
+ // Read QOS logic
+ // {{{
+ // read_qos_lockout will get set if a master with a higher
+ // QOS number is requesting a given slave. It will not
+ // affect existing outstanding packets, but will be used to
+ // prevent further packets from being sent to a given slave.
+ if (!OPT_QOS || NM == 1)
+ begin : READ_NO_QOS
+
+ // If we aren't implementing QOS, then the lockout
+ // signal is never set
+ assign read_qos_lockout[N] = 0;
+
+ end else begin : READ_QOS
+ // {{{
+ // We set lockout if another master (with a higher
+ // QOS) is requesting this slave *and* the slave
+ // channel is currently stalled.
+ reg r_read_qos_lockout;
+
+ initial r_read_qos_lockout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_read_qos_lockout <= 0;
+ else begin
+ r_read_qos_lockout <= 0;
+
+ for(iN=0; iN<NM; iN=iN+1)
+ if (iN != N)
+ begin
+ if (m_arvalid[iN]
+ && !slave_raccepts[N]
+ &&(|(rrequest[iN][NS-1:0]
+ & rdecode[NS-1:0]))
+ &&(m_arqos[N] < m_arqos[iN]))
+ r_read_qos_lockout <= 1;
+ end
+ end
+
+ assign read_qos_lockout[N] = 0;
+ // }}}
+ end
+ // }}}
+
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_RSKID_BUFFERS
+ // {{{
+ assign m_arvalid[N] = 0;
+ assign m_arid[N] = 0;
+ assign m_araddr[N] = 0;
+ assign m_arlen[N] = 0;
+ assign m_arsize[N] = 0;
+ assign m_arburst[N] = 0;
+ assign m_arlock[N] = 0;
+ assign m_arcache[N] = 0;
+ assign m_arprot[N] = 0;
+ assign m_arqos[N] = 0;
+
+ assign read_qos_lockout[N] = 0;
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Channel arbitration
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // wrequested
+ // {{{
+ always @(*)
+ begin : W2_DECONFLICT_WRITE_REQUESTS
+
+ for(iN=0; iN<=NM; iN=iN+1)
+ wrequested[iN] = 0;
+
+ // Vivado may complain about too many bits for wrequested.
+ // This is (currrently) expected. mwindex is used to index
+ // into wrequested, and mwindex has LGNS bits, where LGNS
+ // is $clog2(NS+1) rather than $clog2(NS). The extra bits
+ // are defined to be zeros, but the point is they are defined.
+ // Therefore, no matter what mwindex is, it will always
+ // reference something valid.
+ wrequested[NM] = 0;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ wrequested[0][iM] = 1'b0;
+ for(iN=1; iN<NM ; iN=iN+1)
+ begin
+ // Continue to request any channel with
+ // a grant and pending operations
+ if (wrequest[iN-1][iM] && wgrant[iN-1][iM])
+ wrequested[iN][iM] = 1;
+ if (wrequest[iN-1][iM] && (!mwgrant[iN-1]||mwempty[iN-1]))
+ wrequested[iN][iM] = 1;
+ // Otherwise, if it's already claimed, then
+ // it can't be claimed again
+ if (wrequested[iN-1][iM])
+ wrequested[iN][iM] = 1;
+ end
+ wrequested[NM][iM] = wrequest[NM-1][iM] || wrequested[NM-1][iM];
+ end
+ end
+ // }}}
+
+ // rrequested
+ // {{{
+ always @(*)
+ begin : R2_DECONFLICT_READ_REQUESTS
+
+ for(iN=0; iN<NM ; iN=iN+1)
+ rrequested[iN] = 0;
+
+ // See the note above for wrequested. This applies to
+ // rrequested as well.
+ rrequested[NM] = 0;
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ rrequested[0][iM] = 0;
+ for(iN=1; iN<NM ; iN=iN+1)
+ begin
+ // Continue to request any channel with
+ // a grant and pending operations
+ if (rrequest[iN-1][iM] && rgrant[iN-1][iM])
+ rrequested[iN][iM] = 1;
+ if (rrequest[iN-1][iM] && (!mrgrant[iN-1] || mrempty[iN-1]))
+ rrequested[iN][iM] = 1;
+ // Otherwise, if it's already claimed, then
+ // it can't be claimed again
+ if (rrequested[iN-1][iM])
+ rrequested[iN][iM] = 1;
+ end
+ rrequested[NM][iM] = rrequest[NM-1][iM] || rrequested[NM-1][iM];
+ end
+ end
+ // }}}
+
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : W3_ARBITRATE_WRITE_REQUESTS
+ // {{{
+ reg stay_on_channel;
+ reg requested_channel_is_available;
+ reg leave_channel;
+ reg [LGNS-1:0] requested_index;
+ wire linger;
+ reg [LGNS-1:0] r_mwindex;
+
+ // The basic logic:
+ // 1. If we must stay_on_channel, then nothing changes
+ // 2. If the requested channel isn't available, then no grant
+ // is issued
+ // 3. Otherwise, if we need to leave this channel--such as if
+ // another master is requesting it, then we lose our grant
+
+ // stay_on_channel
+ // {{{
+ // We must stay on the channel if we aren't done working with it
+ // i.e. more writes requested, more acknowledgments expected,
+ // etc.
+ always @(*)
+ begin
+ stay_on_channel = |(wrequest[N][NS:0] & wgrant[N]);
+ if (write_qos_lockout[N])
+ stay_on_channel = 0;
+
+ // We must stay on this channel until we've received
+ // our last acknowledgment signal. Only then can we
+ // switch grants
+ if (mwgrant[N] && !mwempty[N])
+ stay_on_channel = 1;
+
+ // if berr_valid is true, we have a grant to the
+ // internal slave-error channel. While this grant
+ // exists, we cannot issue any others.
+ if (berr_valid[N])
+ stay_on_channel = 1;
+ end
+ // }}}
+
+ // requested_channel_is_available
+ // {{{
+ always @(*)
+ begin
+ // The channel is available to us if 1) we want it,
+ // 2) no one else is using it, and 3) no one earlier
+ // has requested it
+ requested_channel_is_available =
+ |(wrequest[N][NS-1:0] & ~swgrant
+ & ~wrequested[N][NS-1:0]);
+
+ // Of course, the error pseudo-channel is *always*
+ // available to us.
+ if (wrequest[N][NS])
+ requested_channel_is_available = 1;
+
+ // Likewise, if we are the only master, then the
+ // channel is always available on any request
+ if (NM < 2)
+ requested_channel_is_available = m_awvalid[N];
+ end
+ // }}}
+
+ // Linger option, and setting the "linger" flag
+ // {{{
+ // If used, linger will hold on to a given channels grant
+ // for some number of clock ticks after the channel has become
+ // idle. This will spare future requests from the same master
+ // to the same slave from neding to go through the arbitration
+ // clock cycle again--potentially saving a clock period. If,
+ // however, the master in question requests a different slave
+ // or a different master requests this slave, then the linger
+ // option is voided and the grant given up anyway.
+ if (OPT_LINGER == 0)
+ begin : NO_LINGER
+ assign linger = 0;
+ end else begin : WRITE_LINGER
+
+ reg [LGLINGER-1:0] linger_counter;
+ reg r_linger;
+
+ initial r_linger = 0;
+ initial linger_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || wgrant[N][NS])
+ begin
+ r_linger <= 0;
+ linger_counter <= 0;
+ end else if (!mwempty[N] || bskd_valid[N])
+ begin
+ // While the channel is in use, we set the
+ // linger counter
+ linger_counter <= OPT_LINGER;
+ r_linger <= 1;
+ end else if (linger_counter > 0)
+ begin
+ // Otherwise, we decrement it until it reaches
+ // zero
+ r_linger <= (linger_counter > 1);
+ linger_counter <= linger_counter - 1;
+ end else
+ r_linger <= 0;
+
+ assign linger = r_linger;
+ end
+ // }}}
+
+ // leave_channel
+ // {{{
+ // True of another master is requesting access to this slave,
+ // or if we are requesting access to another slave. If QOS
+ // lockout is enabled, then we also leave the channel if a
+ // request with a higher QOS has arrived
+ always @(*)
+ begin
+ leave_channel = 0;
+ if (!m_awvalid[N]
+ && (!linger || wrequested[NM][mwindex[N]]))
+ // Leave the channel after OPT_LINGER counts
+ // of the channel being idle, or when someone
+ // else asks for the channel
+ leave_channel = 1;
+ if (m_awvalid[N] && !wrequest[N][mwindex[N]])
+ // Need to leave this channel to connect
+ // to any other channel
+ leave_channel = 1;
+ if (write_qos_lockout[N])
+ // Need to leave this channel for another higher
+ // priority request
+ leave_channel = 1;
+ end
+ // }}}
+
+ // WRITE GRANT ALLOCATION
+ // {{{
+ // Now that we've done our homework, we can switch grants
+ // if necessary
+ initial wgrant[N] = 0;
+ initial mwgrant[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ wgrant[N] <= 0;
+ mwgrant[N] <= 0;
+ end else if (!stay_on_channel)
+ begin
+ if (requested_channel_is_available)
+ begin
+ // Switch to a new channel
+ mwgrant[N] <= 1'b1;
+ wgrant[N] <= wrequest[N][NS:0];
+ end else if (leave_channel)
+ begin
+ // Revoke the given grant
+ mwgrant[N] <= 1'b0;
+ wgrant[N] <= 0;
+ end
+ end
+ // }}}
+
+ // mwindex (registered)
+ // {{{
+ always @(wrequest[N])
+ begin
+ requested_index = 0;
+ for(iM=0; iM<=NS; iM=iM+1)
+ if (wrequest[N][iM])
+ requested_index= requested_index | iM[LGNS-1:0];
+ end
+
+ // Now for mwindex
+ initial r_mwindex = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!stay_on_channel && requested_channel_is_available)
+ r_mwindex <= requested_index;
+
+ assign mwindex[N] = r_mwindex;
+ // }}}
+
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : EMPTY_WRITE_REQUEST
+
+ assign mwindex[N] = 0;
+ // }}}
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : R3_ARBITRATE_READ_REQUESTS
+ // {{{
+ reg stay_on_channel;
+ reg requested_channel_is_available;
+ reg leave_channel;
+ reg [LGNS-1:0] requested_index;
+ wire linger;
+ reg [LGNS-1:0] r_mrindex;
+
+
+ // The basic logic:
+ // 1. If we must stay_on_channel, then nothing changes
+ // 2. If the requested channel isn't available, then no grant
+ // is issued
+ // 3. Otherwise, if we need to leave this channel--such as if
+ // another master is requesting it, then we lose our grant
+
+ // stay_on_channel
+ // {{{
+ // We must stay on the channel if we aren't done working with it
+ // i.e. more reads requested, more acknowledgments expected,
+ // etc.
+ always @(*)
+ begin
+ stay_on_channel = |(rrequest[N][NS:0] & rgrant[N]);
+ if (read_qos_lockout[N])
+ stay_on_channel = 0;
+
+ // We must stay on this channel until we've received
+ // our last acknowledgment signal. Only then can we
+ // switch grants
+ if (mrgrant[N] && !mrempty[N])
+ stay_on_channel = 1;
+
+ // if we have a grant to the internal slave-error
+ // channel, then we cannot issue a grant to any other
+ // while this grant is active
+ if (rgrant[N][NS] && (!rerr_none[N] || rskd_valid[N]))
+ stay_on_channel = 1;
+ end
+ // }}}
+
+ // requested_channel_is_available
+ // {{{
+ always @(*)
+ begin
+ // The channel is available to us if 1) we want it,
+ // 2) no one else is using it, and 3) no one earlier
+ // has requested it
+ requested_channel_is_available =
+ |(rrequest[N][NS-1:0] & ~srgrant
+ & ~rrequested[N][NS-1:0]);
+
+ // Of course, the error pseudo-channel is *always*
+ // available to us.
+ if (rrequest[N][NS])
+ requested_channel_is_available = 1;
+
+ // Likewise, if we are the only master, then the
+ // channel is always available on any request
+ if (NM < 2)
+ requested_channel_is_available = m_arvalid[N];
+ end
+ // }}}
+
+ // Linger option, and setting the "linger" flag
+ // {{{
+ // If used, linger will hold on to a given channels grant
+ // for some number of clock ticks after the channel has become
+ // idle. This will spare future requests from the same master
+ // to the same slave from neding to go through the arbitration
+ // clock cycle again--potentially saving a clock period. If,
+ // however, the master in question requests a different slave
+ // or a different master requests this slave, then the linger
+ // option is voided and the grant given up anyway.
+ if (OPT_LINGER == 0)
+ begin : NO_LINGER
+ assign linger = 0;
+ end else begin : READ_LINGER
+ reg r_linger;
+ reg [LGLINGER-1:0] linger_counter;
+
+ initial r_linger = 0;
+ initial linger_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || rgrant[N][NS])
+ begin
+ r_linger <= 0;
+ linger_counter <= 0;
+ end else if (!mrempty[N] || rskd_valid[N])
+ begin
+ linger_counter <= OPT_LINGER;
+ r_linger <= 1;
+ end else if (linger_counter > 0)
+ begin
+ r_linger <= (linger_counter > 1);
+ linger_counter <= linger_counter - 1;
+ end else
+ r_linger <= 0;
+
+ assign linger = r_linger;
+ end
+ // }}}
+
+ // leave_channel
+ // {{{
+ // True of another master is requesting access to this slave,
+ // or if we are requesting access to another slave. If QOS
+ // lockout is enabled, then we also leave the channel if a
+ // request with a higher QOS has arrived
+ always @(*)
+ begin
+ leave_channel = 0;
+ if (!m_arvalid[N]
+ && (!linger || rrequested[NM][mrindex[N]]))
+ // Leave the channel after OPT_LINGER counts
+ // of the channel being idle, or when someone
+ // else asks for the channel
+ leave_channel = 1;
+ if (m_arvalid[N] && !rrequest[N][mrindex[N]])
+ // Need to leave this channel to connect
+ // to any other channel
+ leave_channel = 1;
+ if (read_qos_lockout[N])
+ leave_channel = 1;
+ end
+ // }}}
+
+
+ // READ GRANT ALLOCATION
+ // {{{
+ initial rgrant[N] = 0;
+ initial mrgrant[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rgrant[N] <= 0;
+ mrgrant[N] <= 0;
+ end else if (!stay_on_channel)
+ begin
+ if (requested_channel_is_available)
+ begin
+ // Switching channels
+ mrgrant[N] <= 1'b1;
+ rgrant[N] <= rrequest[N][NS:0];
+ end else if (leave_channel)
+ begin
+ mrgrant[N] <= 1'b0;
+ rgrant[N] <= 0;
+ end
+ end
+ // }}}
+
+ // mrindex (registered)
+ // {{{
+ always @(rrequest[N])
+ begin
+ requested_index = 0;
+ for(iM=0; iM<=NS; iM=iM+1)
+ if (rrequest[N][iM])
+ requested_index = requested_index|iM[LGNS-1:0];
+ end
+
+ initial r_mrindex = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!stay_on_channel && requested_channel_is_available)
+ r_mrindex <= requested_index;
+
+ assign mrindex[N] = r_mrindex;
+ // }}}
+
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : EMPTY_READ_REQUEST
+
+ assign mrindex[N] = 0;
+ // }}}
+ end endgenerate
+
+ // Calculate swindex (registered)
+ generate for (M=0; M<NS; M=M+1)
+ begin : W4_SLAVE_WRITE_INDEX
+ // {{{
+ // swindex is a per slave index, containing the index of the
+ // master that has currently won write arbitration and so
+ // has permission to access this slave
+ if (NM <= 1)
+ begin : ONE_MASTER
+
+ // If there's only ever one master, that index is
+ // always the index of the one master.
+ assign swindex[M] = 0;
+
+ end else begin : MULTIPLE_MASTERS
+
+ reg [LGNM-1:0] reqwindex, r_swindex;
+
+ // In the case of multiple masters, we follow the logic
+ // of the arbiter to generate the appropriate index
+ // here, and register it on the next clock cycle. If
+ // no slave has arbitration, the index will remain zero
+ always @(*)
+ begin
+ reqwindex = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if ((!mwgrant[iN] || mwempty[iN])
+ &&(wrequest[iN][M] && !wrequested[iN][M]))
+ reqwindex = reqwindex | iN[LGNM-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!swgrant[M])
+ r_swindex <= reqwindex;
+
+ assign swindex[M] = r_swindex;
+ end
+
+ end for (M=NS; M<NSFULL; M=M+1)
+ begin : EMPTY_WRITE_INDEX
+
+ assign swindex[M] = 0;
+ // }}}
+ end endgenerate
+
+ // Calculate srindex (registered)
+ generate for (M=0; M<NS; M=M+1)
+ begin : R4_SLAVE_READ_INDEX
+ // {{{
+ // srindex is an index to the master that has currently won
+ // read arbitration to the given slave.
+
+ if (NM <= 1)
+ begin : ONE_MASTER
+ // If there's only one master, srindex can always
+ // point to that master--no longic required
+ assign srindex[M] = 0;
+
+ end else begin : MULTIPLE_MASTERS
+
+ reg [LGNM-1:0] reqrindex, r_srindex;
+
+ // In the case of multiple masters, we'll follow the
+ // read arbitration logic to generate the index--first
+ // combinatorially, then we'll register it.
+ always @(*)
+ begin
+ reqrindex = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if ((!mrgrant[iN] || mrempty[iN])
+ &&(rrequest[iN][M] && !rrequested[iN][M]))
+ reqrindex = reqrindex | iN[LGNM-1:0];
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!srgrant[M])
+ r_srindex <= reqrindex;
+
+ assign srindex[M] = r_srindex;
+ end
+
+ end for (M=NS; M<NSFULL; M=M+1)
+ begin : EMPTY_READ_INDEX
+
+ assign srindex[M] = 0;
+ // }}}
+ end endgenerate
+
+ // swgrant and srgrant (combinatorial)
+ generate for(M=0; M<NS; M=M+1)
+ begin : SGRANT
+ // {{{
+
+ // s?grant is a convenience to tell a slave that some master
+ // has won arbitration and so has a grant to that slave.
+
+ // swgrant: write arbitration
+ initial swgrant = 0;
+ always @(*)
+ begin
+ swgrant[M] = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (wgrant[iN][M])
+ swgrant[M] = 1;
+ end
+
+ initial srgrant = 0;
+ // srgrant: read arbitration
+ always @(*)
+ begin
+ srgrant[M] = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (rgrant[iN][M])
+ srgrant[M] = 1;
+ end
+ // }}}
+ end endgenerate
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Generate the signals for the various slaves--the forward channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assign outputs to the various slaves
+ generate for(M=0; M<NS; M=M+1)
+ begin : W5_WRITE_SLAVE_OUTPUTS
+ // {{{
+ reg axi_awvalid;
+ reg [IW-1:0] axi_awid;
+ reg [AW-1:0] axi_awaddr;
+ reg [7:0] axi_awlen;
+ reg [2:0] axi_awsize;
+ reg [1:0] axi_awburst;
+ reg axi_awlock;
+ reg [3:0] axi_awcache;
+ reg [2:0] axi_awprot;
+ reg [3:0] axi_awqos;
+
+ reg axi_wvalid;
+ reg [DW-1:0] axi_wdata;
+ reg [DW/8-1:0] axi_wstrb;
+ reg axi_wlast;
+ //
+ reg axi_bready;
+
+ reg sawstall, swstall;
+ reg awaccepts;
+
+ // Control the slave's AW* channel
+ // {{{
+
+ // Personalize the slave_awaccepts signal
+ always @(*)
+ awaccepts = slave_awaccepts[swindex[M]];
+
+ always @(*)
+ sawstall= (M_AXI_AWVALID[M]&& !M_AXI_AWREADY[M]);
+
+ initial axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !swgrant[M])
+ axi_awvalid <= 0;
+ else if (!sawstall)
+ begin
+ axi_awvalid <= m_awvalid[swindex[M]] &&(awaccepts);
+ end
+
+ initial axi_awid = 0;
+ initial axi_awaddr = 0;
+ initial axi_awlen = 0;
+ initial axi_awsize = 0;
+ initial axi_awburst = 0;
+ initial axi_awlock = 0;
+ initial axi_awcache = 0;
+ initial axi_awprot = 0;
+ initial axi_awqos = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!S_AXI_ARESETN || !swgrant[M]))
+ begin
+ // Under the OPT_LOWPOWER option, we clear all signals
+ // we aren't using
+ axi_awid <= 0;
+ axi_awaddr <= 0;
+ axi_awlen <= 0;
+ axi_awsize <= 0;
+ axi_awburst <= 0;
+ axi_awlock <= 0;
+ axi_awcache <= 0;
+ axi_awprot <= 0;
+ axi_awqos <= 0;
+ end else if (!sawstall)
+ begin
+ if (!OPT_LOWPOWER||(m_awvalid[swindex[M]]&&awaccepts))
+ begin
+ // swindex[M] is defined as 0 above in the
+ // case where NM <= 1
+ axi_awid <= m_awid[ swindex[M]];
+ axi_awaddr <= m_awaddr[ swindex[M]];
+ axi_awlen <= m_awlen[ swindex[M]];
+ axi_awsize <= m_awsize[ swindex[M]];
+ axi_awburst <= m_awburst[swindex[M]];
+ axi_awlock <= m_awlock[ swindex[M]];
+ axi_awcache <= m_awcache[swindex[M]];
+ axi_awprot <= m_awprot[ swindex[M]];
+ axi_awqos <= m_awqos[ swindex[M]];
+ end else begin
+ axi_awid <= 0;
+ axi_awaddr <= 0;
+ axi_awlen <= 0;
+ axi_awsize <= 0;
+ axi_awburst <= 0;
+ axi_awlock <= 0;
+ axi_awcache <= 0;
+ axi_awprot <= 0;
+ axi_awqos <= 0;
+ end
+ end
+ // }}}
+
+ // Control the slave's W* channel
+ // {{{
+ always @(*)
+ swstall = (M_AXI_WVALID[M] && !M_AXI_WREADY[M]);
+
+ initial axi_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !swgrant[M])
+ axi_wvalid <= 0;
+ else if (!swstall)
+ begin
+ axi_wvalid <= (m_wvalid[swindex[M]])
+ &&(slave_waccepts[swindex[M]]);
+ end
+
+ initial axi_wdata = 0;
+ initial axi_wstrb = 0;
+ initial axi_wlast = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ axi_wlast <= 0;
+ end else if (OPT_LOWPOWER && !swgrant[M])
+ begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ axi_wlast <= 0;
+ end else if (!swstall)
+ begin
+ if (!OPT_LOWPOWER || (m_wvalid[swindex[M]]&&slave_waccepts[swindex[M]]))
+ begin
+ // If NM <= 1, swindex[M] is already defined
+ // to be zero above
+ axi_wdata <= m_wdata[swindex[M]];
+ axi_wstrb <= m_wstrb[swindex[M]];
+ axi_wlast <= m_wlast[swindex[M]];
+ end else begin
+ axi_wdata <= 0;
+ axi_wstrb <= 0;
+ axi_wlast <= 0;
+ end
+ end
+ // }}}
+
+ //
+ always @(*)
+ if (!swgrant[M])
+ axi_bready = 1;
+ else
+ axi_bready = bskd_ready[swindex[M]];
+
+ // Combinatorial assigns
+ // {{{
+ assign M_AXI_AWVALID[M] = axi_awvalid;
+ assign M_AXI_AWID[ M*IW +: IW] = axi_awid;
+ assign M_AXI_AWADDR[ M*AW +: AW] = axi_awaddr;
+ assign M_AXI_AWLEN[ M* 8 +: 8] = axi_awlen;
+ assign M_AXI_AWSIZE[ M* 3 +: 3] = axi_awsize;
+ assign M_AXI_AWBURST[M* 2 +: 2] = axi_awburst;
+ assign M_AXI_AWLOCK[ M] = axi_awlock;
+ assign M_AXI_AWCACHE[M* 4 +: 4] = axi_awcache;
+ assign M_AXI_AWPROT[ M* 3 +: 3] = axi_awprot;
+ assign M_AXI_AWQOS[ M* 4 +: 4] = axi_awqos;
+ //
+ //
+ assign M_AXI_WVALID[M] = axi_wvalid;
+ assign M_AXI_WDATA[M*DW +: DW] = axi_wdata;
+ assign M_AXI_WSTRB[M*DW/8 +: DW/8] = axi_wstrb;
+ assign M_AXI_WLAST[M] = axi_wlast;
+ //
+ //
+ assign M_AXI_BREADY[M] = axi_bready;
+ // }}}
+ //
+ // }}}
+ end endgenerate
+
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : R5_READ_SLAVE_OUTPUTS
+ // {{{
+ reg axi_arvalid;
+ reg [IW-1:0] axi_arid;
+ reg [AW-1:0] axi_araddr;
+ reg [7:0] axi_arlen;
+ reg [2:0] axi_arsize;
+ reg [1:0] axi_arburst;
+ reg axi_arlock;
+ reg [3:0] axi_arcache;
+ reg [2:0] axi_arprot;
+ reg [3:0] axi_arqos;
+ //
+ reg axi_rready;
+ wire arstall;
+
+ assign arstall= axi_arvalid && !M_AXI_ARREADY[M];
+
+ initial axi_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !srgrant[M])
+ axi_arvalid <= 0;
+ else if (!arstall)
+ axi_arvalid <= m_arvalid[srindex[M]] && slave_raccepts[srindex[M]];
+ else if (M_AXI_ARREADY[M])
+ axi_arvalid <= 0;
+
+ initial axi_arid = 0;
+ initial axi_araddr = 0;
+ initial axi_arlen = 0;
+ initial axi_arsize = 0;
+ initial axi_arburst = 0;
+ initial axi_arlock = 0;
+ initial axi_arcache = 0;
+ initial axi_arprot = 0;
+ initial axi_arqos = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && (!S_AXI_ARESETN || !srgrant[M]))
+ begin
+ axi_arid <= 0;
+ axi_araddr <= 0;
+ axi_arlen <= 0;
+ axi_arsize <= 0;
+ axi_arburst <= 0;
+ axi_arlock <= 0;
+ axi_arcache <= 0;
+ axi_arprot <= 0;
+ axi_arqos <= 0;
+ end else if (!arstall)
+ begin
+ if (!OPT_LOWPOWER || (m_arvalid[srindex[M]] && slave_raccepts[srindex[M]]))
+ begin
+ // If NM <=1, srindex[M] is defined to be zero
+ axi_arid <= m_arid[ srindex[M]];
+ axi_araddr <= m_araddr[ srindex[M]];
+ axi_arlen <= m_arlen[ srindex[M]];
+ axi_arsize <= m_arsize[ srindex[M]];
+ axi_arburst <= m_arburst[srindex[M]];
+ axi_arlock <= m_arlock[ srindex[M]];
+ axi_arcache <= m_arcache[srindex[M]];
+ axi_arprot <= m_arprot[ srindex[M]];
+ axi_arqos <= m_arqos[ srindex[M]];
+ end else begin
+ axi_arid <= 0;
+ axi_araddr <= 0;
+ axi_arlen <= 0;
+ axi_arsize <= 0;
+ axi_arburst <= 0;
+ axi_arlock <= 0;
+ axi_arcache <= 0;
+ axi_arprot <= 0;
+ axi_arqos <= 0;
+ end
+ end
+
+ always @(*)
+ if (!srgrant[M])
+ axi_rready = 1;
+ else
+ axi_rready = rskd_ready[srindex[M]];
+
+ //
+ assign M_AXI_ARVALID[M] = axi_arvalid;
+ assign M_AXI_ARID[ M*IW +: IW] = axi_arid;
+ assign M_AXI_ARADDR[ M*AW +: AW] = axi_araddr;
+ assign M_AXI_ARLEN[ M* 8 +: 8] = axi_arlen;
+ assign M_AXI_ARSIZE[ M* 3 +: 3] = axi_arsize;
+ assign M_AXI_ARBURST[M* 2 +: 2] = axi_arburst;
+ assign M_AXI_ARLOCK[ M] = axi_arlock;
+ assign M_AXI_ARCACHE[M* 4 +: 4] = axi_arcache;
+ assign M_AXI_ARPROT[ M* 3 +: 3] = axi_arprot;
+ assign M_AXI_ARQOS[ M* 4 +: 4] = axi_arqos;
+ //
+ assign M_AXI_RREADY[M] = axi_rready;
+ //
+ // }}}
+ end endgenerate
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Generate the signals for the various masters--the return channel
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Return values
+ generate for (N=0; N<NM; N=N+1)
+ begin : W6_WRITE_RETURN_CHANNEL
+ // {{{
+ reg [1:0] i_axi_bresp;
+ reg [IW-1:0] i_axi_bid;
+
+ // Write error (no slave selected) state machine
+ // {{{
+ initial berr_valid[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ berr_valid[N] <= 0;
+ else if (wgrant[N][NS] && m_wvalid[N] && m_wlast[N]
+ && slave_waccepts[N])
+ berr_valid[N] <= 1;
+ else if (bskd_ready[N])
+ berr_valid[N] <= 0;
+
+ always @(*)
+ if (berr_valid[N])
+ bskd_valid[N] = 1;
+ else
+ bskd_valid[N] = mwgrant[N]&&m_axi_bvalid[mwindex[N]];
+
+ always @(posedge S_AXI_ACLK)
+ if (m_awvalid[N] && slave_awaccepts[N])
+ berr_id[N] <= m_awid[N];
+
+ always @(*)
+ if (wgrant[N][NS])
+ begin
+ i_axi_bid = berr_id[N];
+ i_axi_bresp = INTERCONNECT_ERROR;
+ end else begin
+ i_axi_bid = m_axi_bid[mwindex[N]];
+ i_axi_bresp = m_axi_bresp[mwindex[N]];
+ end
+ // }}}
+
+ // bskid, the B* channel skidbuffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .DW(IW+2),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(1)
+ // }}}
+ ) bskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(bskd_valid[N]), .o_ready(bskd_ready[N]),
+ .i_data({ i_axi_bid, i_axi_bresp }),
+ .o_valid(S_AXI_BVALID[N]), .i_ready(S_AXI_BREADY[N]),
+ .o_data({ S_AXI_BID[N*IW +: IW], S_AXI_BRESP[N*2 +: 2]})
+ // }}}
+ );
+ // }}}
+ // }}}
+ end endgenerate
+
+ // Return values
+ generate for (N=0; N<NM; N=N+1)
+ begin : R6_READ_RETURN_CHANNEL
+ // {{{
+
+ reg [DW-1:0] i_axi_rdata;
+ reg [IW-1:0] i_axi_rid;
+ reg [2-1:0] i_axi_rresp;
+
+ // generate the read response
+ // {{{
+ // Here we have two choices. We can either generate our
+ // response from the slave itself, or from our internally
+ // generated (no-slave exists) FSM.
+ always @(*)
+ if (rgrant[N][NS])
+ rskd_valid[N] = !rerr_none[N];
+ else
+ rskd_valid[N] = mrgrant[N] && m_axi_rvalid[mrindex[N]];
+
+ always @(*)
+ if (rgrant[N][NS])
+ begin
+ i_axi_rid = rerr_id[N];
+ i_axi_rdata = 0;
+ rskd_rlast[N] = rerr_last[N];
+ i_axi_rresp = INTERCONNECT_ERROR;
+ end else begin
+ i_axi_rid = m_axi_rid[mrindex[N]];
+ i_axi_rdata = m_axi_rdata[mrindex[N]];
+ rskd_rlast[N]= m_axi_rlast[mrindex[N]];
+ i_axi_rresp = m_axi_rresp[mrindex[N]];
+ end
+ // }}}
+
+ // rskid, the outgoing read skidbuffer
+ // {{{
+ // Since our various read signals are all combinatorially
+ // determined, we'll throw them into an outgoing skid buffer
+ // to register them (per spec) and to make it easier to meet
+ // timing.
+ skidbuffer #(
+ // {{{
+ .DW(IW+DW+1+2),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(1)
+ // }}}
+ ) rskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(rskd_valid[N]), .o_ready(rskd_ready[N]),
+ .i_data({ i_axi_rid, i_axi_rdata, rskd_rlast[N], i_axi_rresp }),
+ .o_valid(S_AXI_RVALID[N]), .i_ready(S_AXI_RREADY[N]),
+ .o_data(
+ { S_AXI_RID[N*IW +: IW], S_AXI_RDATA[N*DW +: DW],
+ S_AXI_RLAST[N], S_AXI_RRESP[N*2 +: 2] })
+ // }}}
+ );
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Count pending transactions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate for (N=0; N<NM; N=N+1)
+ begin : W7_COUNT_PENDING_WRITES
+ // {{{
+
+ reg [LGMAXBURST-1:0] awpending, wpending;
+ reg r_wdata_expected;
+
+ // awpending, and the associated flags mwempty and mwfull
+ // {{{
+ // awpending is a count of all of the AW* packets that have
+ // been forwarded to the slave, but for which the slave has
+ // yet to return a B* response. This number can be as large
+ // as (1<<LGMAXBURST)-1. The two associated flags, mwempty
+ // and mwfull, are there to keep us from checking awempty==0
+ // and &awempty respectively.
+ initial awpending = 0;
+ initial mwempty[N] = 1;
+ initial mwfull[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ awpending <= 0;
+ mwempty[N] <= 1;
+ mwfull[N] <= 0;
+ end else case ({(m_awvalid[N] && slave_awaccepts[N]),
+ (bskd_valid[N] && bskd_ready[N])})
+ 2'b01: begin
+ awpending <= awpending - 1;
+ mwempty[N] <= (awpending <= 1);
+ mwfull[N] <= 0;
+ end
+ 2'b10: begin
+ awpending <= awpending + 1;
+ mwempty[N] <= 0;
+ mwfull[N] <= &awpending[LGMAXBURST-1:1];
+ end
+ default: begin end
+ endcase
+
+ // Just so we can access this counter elsewhere, let's make
+ // it available outside of this generate block. (The formal
+ // section uses this.)
+ assign w_mawpending[N] = awpending;
+ // }}}
+
+ // r_wdata_expected and wdata_expected
+ // {{{
+ // This section keeps track of whether or not we are expecting
+ // more W* data from the given burst. It's designed to keep us
+ // from accepting new W* information before the AW* portion
+ // has been routed to the new slave.
+ //
+ // Addition: wpending. wpending counts the number of write
+ // bursts that are pending, based upon the write channel.
+ // Bursts are counted from AWVALID & AWREADY, and decremented
+ // once we see the WVALID && WREADY signal. Packets should
+ // not be accepted without a prior (or concurrent)
+ // AWVALID && AWREADY.
+ initial r_wdata_expected = 0;
+ initial wpending = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ r_wdata_expected <= 0;
+ wpending <= 0;
+ end else case ({(m_awvalid[N] && slave_awaccepts[N]),
+ (m_wvalid[N]&&slave_waccepts[N] && m_wlast[N])})
+ 2'b01: begin
+ r_wdata_expected <= (wpending > 1);
+ wpending <= wpending - 1;
+ end
+ 2'b10: begin
+ wpending <= wpending + 1;
+ r_wdata_expected <= 1;
+ end
+ default: begin end
+ endcase
+
+ assign wdata_expected[N] = r_wdata_expected;
+
+ assign wlasts_pending[N] = wpending;
+ // }}}
+ // }}}
+ end endgenerate
+
+ generate for (N=0; N<NM; N=N+1)
+ begin : R7_COUNT_PENDING_READS
+ // {{{
+
+ reg [LGMAXBURST-1:0] rpending;
+
+ // rpending, and its associated mrempty and mrfull
+ // {{{
+ // rpending counts the number of read transactions that have
+ // been accepted, but for which rlast has yet to be returned.
+ // This specifically counts grants to valid slaves. The error
+ // slave is excluded from this count. mrempty and mrfull have
+ // analogous definitions to mwempty and mwfull, being equal to
+ // rpending == 0 and (&rpending) respectfully.
+ initial rpending = 0;
+ initial mrempty[N] = 1;
+ initial mrfull[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rpending <= 0;
+ mrempty[N]<= 1;
+ mrfull[N] <= 0;
+ end else case ({(m_arvalid[N] && slave_raccepts[N] && !rgrant[N][NS]),
+ (rskd_valid[N] && rskd_ready[N]
+ && rskd_rlast[N] && !rgrant[N][NS])})
+ 2'b01: begin
+ rpending <= rpending - 1;
+ mrempty[N] <= (rpending == 1);
+ mrfull[N] <= 0;
+ end
+ 2'b10: begin
+ rpending <= rpending + 1;
+ mrfull[N] <= &rpending[LGMAXBURST-1:1];
+ mrempty[N] <= 0;
+ end
+ default: begin end
+ endcase
+
+ assign w_mrpending[N] = rpending;
+ // }}}
+
+ // Read error state machine, rerr_outstanding and rerr_id
+ // {{{
+ // rerr_outstanding is the count of read *beats* that remain
+ // to be returned to a master from a non-existent slave.
+ // rerr_last is true on the last of these read beats,
+ // equivalent to rerr_outstanding == 1, and rerr_none is true
+ // when the error state machine is idle
+ initial rerr_outstanding[N] = 0;
+ initial rerr_last[N] = 0;
+ initial rerr_none[N] = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rerr_outstanding[N] <= 0;
+ rerr_last[N] <= 0;
+ rerr_none[N] <= 1;
+ end else if (!rerr_none[N])
+ begin
+ if (!rskd_valid[N] || rskd_ready[N])
+ begin
+ rerr_none[N] <= (rerr_outstanding[N] == 1);
+ rerr_last[N] <= (rerr_outstanding[N] == 2);
+ rerr_outstanding[N] <= rerr_outstanding[N] - 1;
+ end
+ end else if (m_arvalid[N] && rrequest[N][NS]
+ && slave_raccepts[N])
+ begin
+ rerr_none[N] <= 0;
+ rerr_last[N] <= (m_arlen[N] == 0);
+ rerr_outstanding[N] <= m_arlen[N] + 1;
+ end
+
+ // rerr_id is the ARID field of the currently outstanding
+ // error. It's used when generating a read response to a
+ // non-existent slave.
+ initial rerr_id[N] = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN && OPT_LOWPOWER)
+ rerr_id[N] <= 0;
+ else if (m_arvalid[N] && slave_raccepts[N])
+ begin
+ if (rrequest[N][NS] || !OPT_LOWPOWER)
+ // A low-logic definition
+ rerr_id[N] <= m_arid[N];
+ else
+ rerr_id[N] <= 0;
+ end else if (OPT_LOWPOWER && rerr_last[N]
+ && (!rskd_valid[N] || rskd_ready[N]))
+ rerr_id[N] <= 0;
+ // }}}
+
+`ifdef FORMAL
+ always @(*)
+ assert(rerr_none[N] == (rerr_outstanding[N] == 0));
+ always @(*)
+ assert(rerr_last[N] == (rerr_outstanding[N] == 1));
+ always @(*)
+ if (OPT_LOWPOWER && rerr_none[N])
+ assert(rerr_id[N] == 0);
+`endif
+ // }}}
+ end endgenerate
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Partial) Parameter validation
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial begin
+ if (NM == 0) begin
+ $display("At least one master must be defined");
+ $stop;
+ end
+
+ if (NS == 0) begin
+ $display("At least one slave must be defined");
+ $stop;
+ end
+ end
+ // }}}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property verification section
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = LGMAXBURST+9;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Declare signals used for formal checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ // ...
+ //
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Initial/reset value checking
+ // {{{
+ initial assert(NS >= 1);
+ initial assert(NM >= 1);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Check the arbiter signals for consistency
+ // {{{
+ generate for(N=0; N<NM; N=N+1)
+ begin : F1_CHECK_MASTER_GRANTS
+ // {{{
+ // Write grants
+ always @(*)
+ for(iM=0; iM<=NS; iM=iM+1)
+ begin
+ if (wgrant[N][iM])
+ begin
+ assert((wgrant[N] ^ (1<<iM))==0);
+ assert(mwgrant[N]);
+ assert(mwindex[N] == iM);
+ if (iM < NS)
+ begin
+ assert(swgrant[iM]);
+ assert(swindex[iM] == N);
+ end
+ end
+ end
+
+ always @(*)
+ if (mwgrant[N])
+ assert(wgrant[N] != 0);
+
+ always @(*)
+ if (wrequest[N][NS])
+ assert(wrequest[N][NS-1:0] == 0);
+
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && f_past_valid && bskd_valid[N])
+ begin
+ assert($stable(wgrant[N]));
+ assert($stable(mwindex[N]));
+ end
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Read grant checking
+ //
+ always @(*)
+ for(iM=0; iM<=NS; iM=iM+1)
+ begin
+ if (rgrant[N][iM])
+ begin
+ assert((rgrant[N] ^ (1<<iM))==0);
+ assert(mrgrant[N]);
+ assert(mrindex[N] == iM);
+ if (iM < NS)
+ begin
+ assert(srgrant[iM]);
+ assert(srindex[iM] == N);
+ end
+ end
+ end
+
+ always @(*)
+ if (mrgrant[N])
+ assert(rgrant[N] != 0);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && f_past_valid && S_AXI_RVALID[N])
+ begin
+ assert($stable(rgrant[N]));
+ assert($stable(mrindex[N]));
+ if (!rgrant[N][NS])
+ assert(!mrempty[N]);
+ end
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI signaling check, (incoming) master side
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : F2_CHECK_MASTERS
+ // {{{
+ faxi_slave #(
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(DW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_ASSUME_RESET(1'b1),
+ .F_AXI_MAXSTALL(0),
+ .F_AXI_MAXRSTALL(2),
+ .F_AXI_MAXDELAY(0),
+ .F_OPT_READCHECK(0),
+ .F_OPT_NO_RESET(1),
+ .F_LGDEPTH(F_LGDEPTH))
+ mstri(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awid( S_AXI_AWID[ N*IW +:IW]),
+ .i_axi_awaddr( S_AXI_AWADDR[ N*AW +:AW]),
+ .i_axi_awlen( S_AXI_AWLEN[ N* 8 +: 8]),
+ .i_axi_awsize( S_AXI_AWSIZE[ N* 3 +: 3]),
+ .i_axi_awburst(S_AXI_AWBURST[N* 2 +: 2]),
+ .i_axi_awlock( S_AXI_AWLOCK[ N]),
+ .i_axi_awcache(S_AXI_AWCACHE[N* 4 +: 4]),
+ .i_axi_awprot( S_AXI_AWPROT[ N* 3 +: 3]),
+ .i_axi_awqos( S_AXI_AWQOS[ N* 4 +: 4]),
+ .i_axi_awvalid(S_AXI_AWVALID[N]),
+ .i_axi_awready(S_AXI_AWREADY[N]),
+ //
+ .i_axi_wdata( S_AXI_WDATA[ N*DW +: DW]),
+ .i_axi_wstrb( S_AXI_WSTRB[ N*DW/8 +: DW/8]),
+ .i_axi_wlast( S_AXI_WLAST[ N]),
+ .i_axi_wvalid(S_AXI_WVALID[N]),
+ .i_axi_wready(S_AXI_WREADY[N]),
+ //
+ .i_axi_bid( S_AXI_BID[ N*IW +:IW]),
+ .i_axi_bresp( S_AXI_BRESP[ N*2 +: 2]),
+ .i_axi_bvalid(S_AXI_BVALID[N]),
+ .i_axi_bready(S_AXI_BREADY[N]),
+ //
+ .i_axi_arid( S_AXI_ARID[ N*IW +:IW]),
+ .i_axi_arready(S_AXI_ARREADY[N]),
+ .i_axi_araddr( S_AXI_ARADDR[ N*AW +:AW]),
+ .i_axi_arlen( S_AXI_ARLEN[ N* 8 +: 8]),
+ .i_axi_arsize( S_AXI_ARSIZE[ N* 3 +: 3]),
+ .i_axi_arburst(S_AXI_ARBURST[N* 2 +: 2]),
+ .i_axi_arlock( S_AXI_ARLOCK[ N]),
+ .i_axi_arcache(S_AXI_ARCACHE[N* 4 +: 4]),
+ .i_axi_arprot( S_AXI_ARPROT[ N* 3 +: 3]),
+ .i_axi_arqos( S_AXI_ARQOS[ N* 4 +: 4]),
+ .i_axi_arvalid(S_AXI_ARVALID[N]),
+ //
+ //
+ .i_axi_rid( S_AXI_RID[ N*IW +: IW]),
+ .i_axi_rdata( S_AXI_RDATA[ N*DW +: DW]),
+ .i_axi_rresp( S_AXI_RRESP[ N* 2 +: 2]),
+ .i_axi_rlast( S_AXI_RLAST[ N]),
+ .i_axi_rvalid(S_AXI_RVALID[N]),
+ .i_axi_rready(S_AXI_RREADY[N]),
+ //
+ // ...
+ //
+ );
+
+ //
+ // ...
+ //
+
+ //
+ // Check full/empty flags
+ //
+
+ always @(*)
+ begin
+ assert(mwfull[N] == &w_mawpending[N]);
+ assert(mwempty[N] == (w_mawpending[N] == 0));
+ end
+
+ always @(*)
+ begin
+ assert(mrfull[N] == &w_mrpending[N]);
+ assert(mrempty[N] == (w_mrpending[N] == 0));
+ end
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI signaling check, (outgoing) slave side
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ generate for(M=0; M<NS; M=M+1)
+ begin : F3_CHECK_SLAVES
+ // {{{
+ faxi_master #(
+ .C_AXI_ID_WIDTH(IW),
+ .C_AXI_DATA_WIDTH(DW),
+ .C_AXI_ADDR_WIDTH(AW),
+ .F_OPT_ASSUME_RESET(1'b1),
+ .F_AXI_MAXSTALL(2),
+ .F_AXI_MAXRSTALL(0),
+ .F_AXI_MAXDELAY(2),
+ .F_OPT_READCHECK(0),
+ .F_OPT_NO_RESET(1),
+ .F_LGDEPTH(F_LGDEPTH))
+ slvi(.i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awid( M_AXI_AWID[ M*IW+:IW]),
+ .i_axi_awaddr( M_AXI_AWADDR[ M*AW +: AW]),
+ .i_axi_awlen( M_AXI_AWLEN[ M*8 +: 8]),
+ .i_axi_awsize( M_AXI_AWSIZE[ M*3 +: 3]),
+ .i_axi_awburst(M_AXI_AWBURST[M*2 +: 2]),
+ .i_axi_awlock( M_AXI_AWLOCK[ M]),
+ .i_axi_awcache(M_AXI_AWCACHE[M*4 +: 4]),
+ .i_axi_awprot( M_AXI_AWPROT[ M*3 +: 3]),
+ .i_axi_awqos( M_AXI_AWQOS[ M*4 +: 4]),
+ .i_axi_awvalid(M_AXI_AWVALID[M]),
+ .i_axi_awready(M_AXI_AWREADY[M]),
+ //
+ .i_axi_wready(M_AXI_WREADY[M]),
+ .i_axi_wdata( M_AXI_WDATA[ M*DW +: DW]),
+ .i_axi_wstrb( M_AXI_WSTRB[ M*DW/8 +: DW/8]),
+ .i_axi_wlast( M_AXI_WLAST[ M]),
+ .i_axi_wvalid(M_AXI_WVALID[M]),
+ //
+ .i_axi_bid( M_AXI_BID[ M*IW +: IW]),
+ .i_axi_bresp( M_AXI_BRESP[ M*2 +: 2]),
+ .i_axi_bvalid(M_AXI_BVALID[M]),
+ .i_axi_bready(M_AXI_BREADY[M]),
+ //
+ .i_axi_arid( M_AXI_ARID[ M*IW +:IW]),
+ .i_axi_araddr( M_AXI_ARADDR[ M*AW +:AW]),
+ .i_axi_arlen( M_AXI_ARLEN[ M*8 +: 8]),
+ .i_axi_arsize( M_AXI_ARSIZE[ M*3 +: 3]),
+ .i_axi_arburst(M_AXI_ARBURST[M*2 +: 2]),
+ .i_axi_arlock( M_AXI_ARLOCK[ M]),
+ .i_axi_arcache(M_AXI_ARCACHE[M* 4 +: 4]),
+ .i_axi_arprot( M_AXI_ARPROT[ M* 3 +: 3]),
+ .i_axi_arqos( M_AXI_ARQOS[ M* 4 +: 4]),
+ .i_axi_arvalid(M_AXI_ARVALID[M]),
+ .i_axi_arready(M_AXI_ARREADY[M]),
+ //
+ //
+ .i_axi_rresp( M_AXI_RRESP[ M*2 +: 2]),
+ .i_axi_rvalid(M_AXI_RVALID[M]),
+ .i_axi_rdata( M_AXI_RDATA[ M*DW +: DW]),
+ .i_axi_rready(M_AXI_RREADY[M]),
+ .i_axi_rlast( M_AXI_RLAST[ M]),
+ .i_axi_rid( M_AXI_RID[ M*IW +: IW]),
+ //
+ // ...
+ //
+ );
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (M_AXI_AWVALID[M])
+ assert(((M_AXI_AWADDR[M*AW +:AW]^SLAVE_ADDR[M*AW +:AW])
+ & SLAVE_MASK[M*AW +: AW]) == 0);
+
+ always @(*)
+ if (M_AXI_ARVALID[M])
+ assert(((M_AXI_ARADDR[M*AW +:AW]^SLAVE_ADDR[M*AW +:AW])
+ & SLAVE_MASK[M*AW +: AW]) == 0);
+ // }}}
+ end endgenerate
+ // }}}
+
+ // m_axi_* convenience signals
+ // {{{
+ // ...
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ...
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : // ...
+ // {{{
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Double buffer checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : F4_DOUBLE_BUFFER_CHECKS
+ // {{{
+ // ...
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Can every master reach every slave?
+ // Can things transition without dropping the request line(s)?
+ generate for(N=0; N<NM; N=N+1)
+ begin : F5_COVER_CONNECTIVITY_FROM_MASTER
+ // {{{
+ // ...
+ // }}}
+ end endgenerate
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Focused check: How fast can one master talk to each of the slaves?
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // ...
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Focused check: How fast can one master talk to a particular slave?
+ // We'll pick master 1 and slave 1.
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // ...
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Poor man's cover check
+ // {{{
+ // ...
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Negation check
+ // {{{
+ // Pick a particular value. Assume the value doesn't show up on the
+ // input. Prove it doesn't show up on the output. This will check for
+ // ...
+ // 1. Stuck bits on the output channel
+ // 2. Cross-talk between channels
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // ...
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Artificially constraining assumptions
+ // {{{
+ // Ideally, this section should be empty--there should be no
+ // assumptions here. The existence of these assumptions should
+ // give you an idea of where I'm at with this project.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : F6_LIMITING_ASSUMPTIONS
+
+ if (!OPT_WRITES)
+ begin
+ always @(*)
+ assume(S_AXI_AWVALID[N] == 0);
+ always @(*)
+ assert(wgrant[N] == 0);
+ always @(*)
+ assert(mwgrant[N] == 0);
+ always @(*)
+ assert(S_AXI_BVALID[N]== 0);
+ end
+
+ if (!OPT_READS)
+ begin
+ always @(*)
+ assume(S_AXI_ARVALID [N]== 0);
+ always @(*)
+ assert(rgrant[N] == 0);
+ always @(*)
+ assert(S_AXI_RVALID[N] == 0);
+ end
+
+ end endgenerate
+
+ always@(*)
+ assert(OPT_READS | OPT_WRITES);
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/axixclk.v b/rtl/wb2axip/axixclk.v
new file mode 100644
index 0000000..670f606
--- /dev/null
+++ b/rtl/wb2axip/axixclk.v
@@ -0,0 +1,312 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axixclk.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Cross AXI clock domains
+//
+// Performance:
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axixclk #(
+ // {{{
+ parameter integer C_S_AXI_ID_WIDTH = 2,
+ parameter integer C_S_AXI_DATA_WIDTH = 32,
+ parameter integer C_S_AXI_ADDR_WIDTH = 6,
+ // Some useful short-hand definitions
+ // localparam AW = C_S_AXI_ADDR_WIDTH,
+ // localparam DW = C_S_AXI_DATA_WIDTH,
+ // localparam IW = C_S_AXI_ID_WIDTH,
+ //
+ parameter [0:0] OPT_WRITE_ONLY = 1'b0,
+ parameter [0:0] OPT_READ_ONLY = 1'b0,
+ parameter XCLOCK_FFS = 2,
+ parameter LGFIFO = 5
+ // }}}
+ ) (
+ // {{{
+ // Users to add ports here
+
+ // User ports ends
+ // Do not modify the ports beyond this line
+
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
+ input wire [7 : 0] S_AXI_AWLEN,
+ input wire [2 : 0] S_AXI_AWSIZE,
+ input wire [1 : 0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [3 : 0] S_AXI_AWCACHE,
+ input wire [2 : 0] S_AXI_AWPROT,
+ input wire [3 : 0] S_AXI_AWQOS,
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ //
+ input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
+ input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ //
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
+ output wire [1 : 0] S_AXI_BRESP,
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ //
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
+ input wire [7 : 0] S_AXI_ARLEN,
+ input wire [2 : 0] S_AXI_ARSIZE,
+ input wire [1 : 0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3 : 0] S_AXI_ARCACHE,
+ input wire [2 : 0] S_AXI_ARPROT,
+ input wire [3 : 0] S_AXI_ARQOS,
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ //
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
+ output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
+ output wire [1 : 0] S_AXI_RRESP,
+ output wire S_AXI_RLAST,
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+
+ //
+ // Downstream port
+ //
+ input wire M_AXI_ACLK,
+ output wire M_AXI_ARESETN,
+ //
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] M_AXI_AWID,
+ output wire [C_S_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
+ output wire [7 : 0] M_AXI_AWLEN,
+ output wire [2 : 0] M_AXI_AWSIZE,
+ output wire [1 : 0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ output wire [3 : 0] M_AXI_AWCACHE,
+ output wire [2 : 0] M_AXI_AWPROT,
+ output wire [3 : 0] M_AXI_AWQOS,
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ //
+ output wire [C_S_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA,
+ output wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ //
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] M_AXI_BID,
+ input wire [1 : 0] M_AXI_BRESP,
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ //
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] M_AXI_ARID,
+ output wire [C_S_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,
+ output wire [7 : 0] M_AXI_ARLEN,
+ output wire [2 : 0] M_AXI_ARSIZE,
+ output wire [1 : 0] M_AXI_ARBURST,
+ output wire M_AXI_ARLOCK,
+ output wire [3 : 0] M_AXI_ARCACHE,
+ output wire [2 : 0] M_AXI_ARPROT,
+ output wire [3 : 0] M_AXI_ARQOS,
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ //
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] M_AXI_RID,
+ input wire [C_S_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,
+ input wire [1 : 0] M_AXI_RRESP,
+ input wire M_AXI_RLAST,
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY
+ // }}}
+ );
+
+ reg [2:0] mreset;
+
+ (* ASYNC_REG = "TRUE" *) initial mreset = 3'b000;
+ always @(posedge M_AXI_ACLK, negedge S_AXI_ARESETN)
+ if (!S_AXI_ARESETN)
+ mreset <= 3'b000;
+ else
+ mreset <= { mreset[1:0], 1'b1 };
+
+ assign M_AXI_ARESETN = mreset[2];
+
+ generate if (OPT_READ_ONLY)
+ begin : READ_ONLY
+
+ assign M_AXI_AWID = 0;
+ assign M_AXI_AWADDR = 0;
+ assign M_AXI_AWLEN = 0;
+ assign M_AXI_AWSIZE = 0;
+ assign M_AXI_AWBURST= 0;
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE= 0;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+ // Either way we do these we're wrong, so don't try accessing
+ // the write side of the bus when OPT_READ_ONLY is set or your
+ // design will hang.
+
+ assign M_AXI_AWVALID = 1'b0;
+ assign S_AXI_AWREADY = 1'b0;
+
+ assign M_AXI_WDATA = 0;
+ assign M_AXI_WSTRB = 0;
+ assign M_AXI_WLAST = 0;
+
+ assign M_AXI_WVALID = 1'b0;
+ assign S_AXI_WREADY = 1'b0;
+
+ assign S_AXI_BID = 0;
+ assign S_AXI_BRESP = 2'b11;
+
+ assign M_AXI_BREADY = 1'b0;
+ assign S_AXI_BVALID = 1'b0;
+
+ end else begin : WRITE_FIFO
+ wire awfull, awempty, wfull, wempty, bfull, bempty;
+
+ afifo #(.LGFIFO(LGFIFO),
+ .NFF(XCLOCK_FFS),
+ .WIDTH(C_S_AXI_ID_WIDTH + C_S_AXI_ADDR_WIDTH
+ + 8 + 3 + 2 + 1 + 4 + 3 + 4))
+ awfifo(S_AXI_ACLK, S_AXI_ARESETN, S_AXI_AWVALID&& S_AXI_AWREADY,
+ { S_AXI_AWID, S_AXI_AWADDR,
+ S_AXI_AWLEN, S_AXI_AWSIZE, S_AXI_AWBURST,
+ S_AXI_AWLOCK,
+ S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS },
+ awfull,
+ M_AXI_ACLK, M_AXI_ARESETN, M_AXI_AWREADY,
+ { M_AXI_AWID, M_AXI_AWADDR,
+ M_AXI_AWLEN, M_AXI_AWSIZE, M_AXI_AWBURST,
+ M_AXI_AWLOCK,
+ M_AXI_AWCACHE, M_AXI_AWPROT, M_AXI_AWQOS },
+ awempty);
+
+ assign M_AXI_AWVALID = !awempty;
+ assign S_AXI_AWREADY = !awfull;
+
+ afifo #(.LGFIFO(LGFIFO),
+ .NFF(XCLOCK_FFS),
+ .WIDTH(C_S_AXI_DATA_WIDTH + C_S_AXI_DATA_WIDTH/8 + 1))
+ wfifo(S_AXI_ACLK, S_AXI_ARESETN, S_AXI_WVALID&& S_AXI_WREADY,
+ { S_AXI_WDATA, S_AXI_WSTRB, S_AXI_WLAST },
+ wfull,
+ M_AXI_ACLK, M_AXI_ARESETN, M_AXI_WREADY,
+ { M_AXI_WDATA, M_AXI_WSTRB, M_AXI_WLAST },
+ wempty);
+
+ assign M_AXI_WVALID = !wempty;
+ assign S_AXI_WREADY = !wfull;
+
+ afifo #(.LGFIFO(LGFIFO),
+ .NFF(XCLOCK_FFS),
+ .WIDTH(C_S_AXI_ID_WIDTH + 2))
+ bfifo(M_AXI_ACLK, M_AXI_ARESETN, M_AXI_BVALID&& M_AXI_BREADY,
+ { M_AXI_BID, M_AXI_BRESP }, bfull,
+ S_AXI_ACLK, S_AXI_ARESETN, S_AXI_BREADY,
+ { S_AXI_BID, S_AXI_BRESP }, bempty);
+
+ assign S_AXI_BVALID = !bempty;
+ assign M_AXI_BREADY = !bfull;
+ end endgenerate
+
+ generate if (OPT_WRITE_ONLY)
+ begin : NO_READS
+
+ assign M_AXI_ARID = 0;
+ assign M_AXI_ARADDR = 0;
+ assign M_AXI_ARLEN = 0;
+ assign M_AXI_ARSIZE = 0;
+ assign M_AXI_ARBURST= 0;
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE= 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_ARQOS = 0;
+ // Either way we do these we're wrong, so don't try accessing
+ // the write side of the bus when OPT_READ_ONLY is set or your
+ // design will hang.
+
+ assign M_AXI_ARVALID = 1'b0;
+ assign S_AXI_ARREADY = 1'b0;
+
+ assign S_AXI_RID = 0;
+ assign S_AXI_RDATA = 2'b11;
+ assign S_AXI_RLAST = 1'b1;
+ assign S_AXI_RRESP = 2'b11;
+
+ assign M_AXI_RREADY = 1'b0;
+ assign S_AXI_RVALID = 1'b0;
+
+ end else begin : READ_FIFO
+ wire arfull, arempty, rfull, rempty;
+
+ afifo #(.LGFIFO(LGFIFO),
+ .NFF(XCLOCK_FFS),
+ .WIDTH(C_S_AXI_ID_WIDTH + C_S_AXI_ADDR_WIDTH
+ + 8 + 3 + 2 + 1 + 4 + 3 + 4))
+ arfifo(S_AXI_ACLK, S_AXI_ARESETN, S_AXI_ARVALID&& S_AXI_ARREADY,
+ { S_AXI_ARID, S_AXI_ARADDR,
+ S_AXI_ARLEN, S_AXI_ARSIZE, S_AXI_ARBURST,
+ S_AXI_ARLOCK,
+ S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS },
+ arfull,
+ M_AXI_ACLK, M_AXI_ARESETN, M_AXI_ARREADY,
+ { M_AXI_ARID, M_AXI_ARADDR,
+ M_AXI_ARLEN, M_AXI_ARSIZE, M_AXI_ARBURST,
+ M_AXI_ARLOCK,
+ M_AXI_ARCACHE, M_AXI_ARPROT, M_AXI_ARQOS },
+ arempty);
+
+ assign M_AXI_ARVALID = !arempty;
+ assign S_AXI_ARREADY = !arfull;
+
+
+ afifo #(.LGFIFO(LGFIFO),
+ .NFF(XCLOCK_FFS),
+ .WIDTH(C_S_AXI_ID_WIDTH + C_S_AXI_DATA_WIDTH+3))
+ rfifo(M_AXI_ACLK, M_AXI_ARESETN, M_AXI_RVALID&& M_AXI_RREADY,
+ { M_AXI_RID, M_AXI_RDATA, M_AXI_RLAST, M_AXI_RRESP },
+ rfull,
+ S_AXI_ACLK, S_AXI_ARESETN, S_AXI_RREADY,
+ { S_AXI_RID, S_AXI_RDATA, S_AXI_RLAST, S_AXI_RRESP },
+ rempty);
+
+ assign S_AXI_RVALID = !rempty;
+ assign M_AXI_RREADY = !rfull;
+
+ end endgenerate
+
+endmodule
diff --git a/rtl/wb2axip/axlite2wbsp.v b/rtl/wb2axip/axlite2wbsp.v
new file mode 100644
index 0000000..b11b365
--- /dev/null
+++ b/rtl/wb2axip/axlite2wbsp.v
@@ -0,0 +1,568 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axlite2wbsp.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Take an AXI lite input, and convert it into WB.
+//
+// We'll treat AXI as two separate busses: one for writes, another for
+// reads, further, we'll insist that the two channels AXI uses for writes
+// combine into one channel for our purposes.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module axlite2wbsp #(
+ // {{{
+ parameter C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
+ parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width
+ parameter LGFIFO = 4,
+`ifdef FORMAL
+ parameter F_MAXSTALL = 3,
+ parameter F_MAXDELAY = 3,
+`endif
+ parameter [0:0] OPT_READONLY = 1'b0,
+ parameter [0:0] OPT_WRITEONLY = 1'b0,
+ localparam AXILLSB = $clog2(C_AXI_DATA_WIDTH/8)
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, // System clock
+ input wire i_axi_reset_n,
+
+ // AXI write address channel signals
+ // {{{
+ input wire i_axi_awvalid,
+ output wire o_axi_awready,
+ input wire [C_AXI_ADDR_WIDTH-1:0] i_axi_awaddr,
+ input wire [2:0] i_axi_awprot,
+ // }}}
+ // AXI write data channel signals
+ // {{{
+ input wire i_axi_wvalid,
+ output wire o_axi_wready,
+ input wire [C_AXI_DATA_WIDTH-1:0] i_axi_wdata,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] i_axi_wstrb,
+ // }}}
+ // AXI write response channel signals
+ // {{{
+ output wire o_axi_bvalid,
+ input wire i_axi_bready,
+ output wire [1:0] o_axi_bresp,
+ // }}}
+ // AXI read address channel signals
+ // {{{
+ input wire i_axi_arvalid,
+ output wire o_axi_arready,
+ input wire [C_AXI_ADDR_WIDTH-1:0] i_axi_araddr,
+ input wire [2:0] i_axi_arprot,
+ // }}}
+ // AXI read data channel signals
+ // {{{
+ output wire o_axi_rvalid,
+ input wire i_axi_rready,
+ output wire [C_AXI_DATA_WIDTH-1:0] o_axi_rdata,
+ output wire [1:0] o_axi_rresp,
+ // }}}
+ // Wishbone signals
+ // {{{
+ // We'll share the clock and the reset
+ output wire o_reset,
+ output wire o_wb_cyc,
+ output wire o_wb_stb,
+ output wire o_wb_we,
+ output wire [C_AXI_ADDR_WIDTH-AXILLSB-1:0] o_wb_addr,
+ output wire [C_AXI_DATA_WIDTH-1:0] o_wb_data,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] o_wb_sel,
+ input wire i_wb_stall,
+ input wire i_wb_ack,
+ input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
+ input wire i_wb_err
+ // }}}
+ // }}}
+ );
+
+ // Local definitions
+ // {{{
+ localparam DW = C_AXI_DATA_WIDTH;
+ localparam AW = C_AXI_ADDR_WIDTH-AXILLSB;
+ //
+ //
+ //
+
+ wire [(AW-1):0] w_wb_addr, r_wb_addr;
+ wire [(DW-1):0] w_wb_data;
+ wire [(DW/8-1):0] w_wb_sel, r_wb_sel;
+ wire r_wb_err, r_wb_cyc, r_wb_stb, r_wb_stall, r_wb_ack;
+ wire w_wb_err, w_wb_cyc, w_wb_stb, w_wb_stall, w_wb_ack;
+
+ // verilator lint_off UNUSED
+ wire r_wb_we, w_wb_we;
+
+ assign r_wb_we = 1'b0;
+ assign w_wb_we = 1'b1;
+ // verilator lint_on UNUSED
+
+`ifdef FORMAL
+ // {{{
+ // Verilator lint_off UNUSED
+ localparam F_LGDEPTH = LGFIFO+1;
+ wire [LGFIFO:0] f_wr_fifo_first, f_rd_fifo_first,
+ f_wr_fifo_mid, f_rd_fifo_mid,
+ f_wr_fifo_last, f_rd_fifo_last;
+ // Verilator lint_on UNUSED
+ wire [(F_LGDEPTH-1):0] f_wb_nreqs, f_wb_nacks,
+ f_wb_outstanding;
+ wire [(F_LGDEPTH-1):0] f_wb_wr_nreqs, f_wb_wr_nacks,
+ f_wb_wr_outstanding;
+ wire [(F_LGDEPTH-1):0] f_wb_rd_nreqs, f_wb_rd_nacks,
+ f_wb_rd_outstanding;
+ wire f_pending_awvalid, f_pending_wvalid;
+ // }}}
+`endif
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite write channel to WB processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (!OPT_READONLY)
+ begin : AXI_WR
+ // {{{
+ axilwr2wbsp #(
+ // {{{
+ // .F_LGDEPTH(F_LGDEPTH),
+ // .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), // .AW(AW),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axi_write_decoder (
+ // {{{
+ .i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
+ //
+ .i_axi_awvalid(i_axi_awvalid),
+ .o_axi_awready(o_axi_awready),
+ .i_axi_awaddr( i_axi_awaddr),
+ .i_axi_awprot( i_axi_awprot),
+ //
+ .i_axi_wvalid( i_axi_wvalid),
+ .o_axi_wready( o_axi_wready),
+ .i_axi_wdata( i_axi_wdata),
+ .i_axi_wstrb( i_axi_wstrb),
+ //
+ .o_axi_bvalid(o_axi_bvalid),
+ .i_axi_bready(i_axi_bready),
+ .o_axi_bresp(o_axi_bresp),
+ //
+ .o_wb_cyc( w_wb_cyc),
+ .o_wb_stb( w_wb_stb),
+ .o_wb_addr( w_wb_addr),
+ .o_wb_data( w_wb_data),
+ .o_wb_sel( w_wb_sel),
+ .i_wb_stall(w_wb_stall),
+ .i_wb_ack( w_wb_ack),
+ .i_wb_err( w_wb_err)
+`ifdef FORMAL
+ // {{{
+ ,
+ .f_first(f_wr_fifo_first),
+ .f_mid( f_wr_fifo_mid),
+ .f_last( f_wr_fifo_last),
+ .f_wpending({ f_pending_awvalid, f_pending_wvalid })
+ // }}}
+`endif
+ // }}}
+ );
+ // }}}
+ end else begin : EMPTY_WRITES
+ // {{{
+ assign w_wb_cyc = 0;
+ assign w_wb_stb = 0;
+ assign w_wb_addr = 0;
+ assign w_wb_data = 0;
+ assign w_wb_sel = 0;
+ assign o_axi_awready = 0;
+ assign o_axi_wready = 0;
+ assign o_axi_bvalid = (i_axi_wvalid);
+ assign o_axi_bresp = 2'b11;
+`ifdef FORMAL
+ assign f_wr_fifo_first = 0;
+ assign f_wr_fifo_mid = 0;
+ assign f_wr_fifo_last = 0;
+ assign f_pending_awvalid=0;
+ assign f_pending_wvalid =0;
+`endif
+ // }}}
+ end endgenerate
+ assign w_wb_we = 1'b1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite read channel to WB processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (!OPT_WRITEONLY)
+ begin : AXI_RD
+ // {{{
+ axilrd2wbsp #(
+ // {{{
+ // .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .LGFIFO(LGFIFO)
+ // }}}
+ ) axi_read_decoder(
+ // {{{
+ .i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
+ //
+ .i_axi_arvalid(i_axi_arvalid),
+ .o_axi_arready(o_axi_arready),
+ .i_axi_araddr( i_axi_araddr),
+ .i_axi_arprot( i_axi_arprot),
+ //
+ .o_axi_rvalid(o_axi_rvalid),
+ .i_axi_rready(i_axi_rready),
+ .o_axi_rdata( o_axi_rdata),
+ .o_axi_rresp( o_axi_rresp),
+ //
+ .o_wb_cyc( r_wb_cyc),
+ .o_wb_stb( r_wb_stb),
+ .o_wb_addr( r_wb_addr),
+ .o_wb_sel( r_wb_sel),
+ .i_wb_stall(r_wb_stall),
+ .i_wb_ack( r_wb_ack),
+ .i_wb_data( i_wb_data),
+ .i_wb_err( r_wb_err)
+`ifdef FORMAL
+ // {{{
+ ,
+ .f_first(f_rd_fifo_first),
+ .f_mid( f_rd_fifo_mid),
+ .f_last( f_rd_fifo_last)
+ // }}}
+`endif
+ // }}}
+ );
+ // }}}
+ end else begin : EMPTY_READS
+ // {{{
+ assign r_wb_cyc = 0;
+ assign r_wb_stb = 0;
+ assign r_wb_addr = 0;
+ //
+ assign o_axi_arready = 1'b1;
+ assign o_axi_rvalid = (i_axi_arvalid)&&(o_axi_arready);
+ assign o_axi_rresp = (i_axi_arvalid) ? 2'b11 : 2'b00;
+ assign o_axi_rdata = 0;
+`ifdef FORMAL
+ assign f_rd_fifo_first = 0;
+ assign f_rd_fifo_mid = 0;
+ assign f_rd_fifo_last = 0;
+`endif
+ // }}}
+ end endgenerate
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The arbiter that pastes the two sides together
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ generate if (OPT_READONLY)
+ begin : ARB_RD
+ // {{{
+ assign o_wb_cyc = r_wb_cyc;
+ assign o_wb_stb = r_wb_stb;
+ assign o_wb_we = 1'b0;
+ assign o_wb_addr = r_wb_addr;
+ assign o_wb_data = 32'h0;
+ assign o_wb_sel = r_wb_sel;
+ assign r_wb_ack = i_wb_ack;
+ assign r_wb_stall= i_wb_stall;
+ assign r_wb_ack = i_wb_ack;
+ assign r_wb_err = i_wb_err;
+
+`ifdef FORMAL
+ fwb_master #(.DW(DW), .AW(AW),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_STALL(F_MAXSTALL),
+ .F_MAX_ACK_DELAY(F_MAXDELAY))
+ f_wb(i_clk, !i_axi_reset_n,
+ o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
+ o_wb_sel,
+ i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
+ f_wb_nreqs, f_wb_nacks, f_wb_outstanding);
+
+ assign f_wb_rd_nreqs = f_wb_nreqs;
+ assign f_wb_rd_nacks = f_wb_nacks;
+ assign f_wb_rd_outstanding = f_wb_outstanding;
+ //
+ assign f_wb_wr_nreqs = 0;
+ assign f_wb_wr_nacks = 0;
+ assign f_wb_wr_outstanding = 0;
+`endif
+ // }}}
+ end else if (OPT_WRITEONLY)
+ begin : ARB_WR
+ // {{{
+ assign o_wb_cyc = w_wb_cyc;
+ assign o_wb_stb = w_wb_stb;
+ assign o_wb_we = 1'b1;
+ assign o_wb_addr = w_wb_addr;
+ assign o_wb_data = w_wb_data;
+ assign o_wb_sel = w_wb_sel;
+ assign w_wb_ack = i_wb_ack;
+ assign w_wb_stall= i_wb_stall;
+ assign w_wb_ack = i_wb_ack;
+ assign w_wb_err = i_wb_err;
+
+`ifdef FORMAL
+ fwb_master #(.DW(DW), .AW(AW),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_STALL(F_MAXSTALL),
+ .F_MAX_ACK_DELAY(F_MAXDELAY))
+ f_wb(i_clk, !i_axi_reset_n,
+ o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
+ o_wb_sel,
+ i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
+ f_wb_nreqs, f_wb_nacks, f_wb_outstanding);
+
+ assign f_wb_wr_nreqs = f_wb_nreqs;
+ assign f_wb_wr_nacks = f_wb_nacks;
+ assign f_wb_wr_outstanding = f_wb_outstanding;
+ //
+ assign f_wb_rd_nreqs = 0;
+ assign f_wb_rd_nacks = 0;
+ assign f_wb_rd_outstanding = 0;
+`endif
+ // }}}
+ end else begin : ARB_WB
+ // {{{
+ wbarbiter #(
+ // {{{
+ .DW(DW), .AW(AW)
+`ifdef FORMAL
+ , .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_STALL(F_MAXSTALL),
+ .F_MAX_ACK_DELAY(F_MAXDELAY)
+`endif
+ // }}}
+ ) readorwrite(
+ // {{{
+ .i_clk(i_clk), .i_reset(!i_axi_reset_n),
+ // Channel A - Reads
+ // {{{
+ .i_a_cyc(r_wb_cyc), .i_a_stb(r_wb_stb),
+ .i_a_we(1'b0),
+ .i_a_adr(r_wb_addr),
+ .i_a_dat(w_wb_data),
+ .i_a_sel(r_wb_sel),
+ .o_a_stall(r_wb_stall),
+ .o_a_ack(r_wb_ack),
+ .o_a_err(r_wb_err),
+ // }}}
+ // Channel B
+ // {{{
+ .i_b_cyc(w_wb_cyc), .i_b_stb(w_wb_stb),
+ .i_b_we(1'b1),
+ .i_b_adr(w_wb_addr),
+ .i_b_dat(w_wb_data),
+ .i_b_sel(w_wb_sel),
+ .o_b_stall(w_wb_stall),
+ .o_b_ack(w_wb_ack),
+ .o_b_err(w_wb_err),
+ // }}}
+ // Arbitrated outgoing channel
+ // {{{
+ .o_cyc(o_wb_cyc), .o_stb(o_wb_stb), .o_we(o_wb_we),
+ .o_adr(o_wb_addr),
+ .o_dat(o_wb_data),
+ .o_sel(o_wb_sel),
+ .i_stall(i_wb_stall),
+ .i_ack(i_wb_ack),
+ .i_err(i_wb_err)
+ // }}}
+`ifdef FORMAL
+ // {{{
+ ,
+ .f_nreqs(f_wb_nreqs), .f_nacks(f_wb_nacks),
+ .f_outstanding(f_wb_outstanding),
+ .f_a_nreqs(f_wb_rd_nreqs), .f_a_nacks(f_wb_rd_nacks),
+ .f_a_outstanding(f_wb_rd_outstanding),
+ .f_b_nreqs(f_wb_wr_nreqs), .f_b_nacks(f_wb_wr_nacks),
+ .f_b_outstanding(f_wb_wr_outstanding)
+ // }}}
+`endif
+ // }}}
+ );
+ // }}}
+ end endgenerate
+ // }}}
+ assign o_reset = (i_axi_reset_n == 1'b0);
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Formal declarations
+ // {{{
+ reg f_past_valid;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+
+ wire [(F_LGDEPTH-1):0] f_axi_rd_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_awr_outstanding;
+
+ wire [LGFIFO:0] f_awr_fifo_axi_used,
+ f_rd_fifo_axi_used;
+ // }}}
+
+ initial assume(!i_axi_reset_n);
+ always @(*)
+ if (!f_past_valid)
+ assume(!i_axi_reset_n);
+
+ faxil_slave #(
+ // {{{
+ // .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_AXI_MAXWAIT(0),
+ .F_AXI_MAXDELAY(0)
+ // }}}
+ ) f_axi(
+ // {{{
+ .i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
+ // AXI write address channnel
+ .i_axi_awvalid(i_axi_awvalid),
+ .i_axi_awready(o_axi_awready),
+ .i_axi_awaddr( i_axi_awaddr),
+ .i_axi_awprot( i_axi_awprot),
+ // AXI write data channel
+ .i_axi_wvalid( i_axi_wvalid),
+ .i_axi_wready( o_axi_wready),
+ .i_axi_wdata( i_axi_wdata),
+ .i_axi_wstrb( i_axi_wstrb),
+ // AXI write acknowledgement channel
+ .i_axi_bvalid(o_axi_bvalid),
+ .i_axi_bready(i_axi_bready),
+ .i_axi_bresp( o_axi_bresp),
+ // AXI read address channel
+ .i_axi_arvalid(i_axi_arvalid),
+ .i_axi_arready(o_axi_arready),
+ .i_axi_araddr( i_axi_araddr),
+ .i_axi_arprot( i_axi_arprot),
+ // AXI read data return
+ .i_axi_rvalid( o_axi_rvalid),
+ .i_axi_rready( i_axi_rready),
+ .i_axi_rdata( o_axi_rdata),
+ .i_axi_rresp( o_axi_rresp),
+ // Quantify where we are within a transaction
+ .f_axi_rd_outstanding( f_axi_rd_outstanding),
+ .f_axi_wr_outstanding( f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding)
+ // }}}
+ );
+
+ assign f_awr_fifo_axi_used = f_wr_fifo_first - f_wr_fifo_last;
+ assign f_rd_fifo_axi_used = f_rd_fifo_first - f_rd_fifo_last;
+
+ always @(*)
+ begin
+ assert(f_axi_rd_outstanding == f_rd_fifo_axi_used);
+ assert(f_axi_awr_outstanding == f_awr_fifo_axi_used+ (f_pending_awvalid?1:0));
+ assert(f_axi_wr_outstanding == f_awr_fifo_axi_used+ (f_pending_wvalid?1:0));
+ end
+
+ always @(*)
+ if (OPT_READONLY)
+ begin
+ assert(f_axi_awr_outstanding == 0);
+ assert(f_axi_wr_outstanding == 0);
+ end
+
+ always @(*)
+ if (OPT_WRITEONLY)
+ begin
+ assert(f_axi_rd_outstanding == 0);
+ end
+
+ //
+ initial assert((!OPT_READONLY)||(!OPT_WRITEONLY));
+
+ always @(*)
+ if (OPT_READONLY)
+ begin
+ assume(i_axi_awvalid == 0);
+ assume(i_axi_wvalid == 0);
+
+ assert(o_axi_bvalid == 0);
+ end
+
+ always @(*)
+ if (OPT_WRITEONLY)
+ begin
+ assume(i_axi_arvalid == 0);
+ assert(o_axi_rvalid == 0);
+ end
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_formal;
+ assign unused_formal = &{ 1'b0, f_wb_nreqs, f_wb_nacks,
+ f_wb_outstanding, f_wb_wr_nreqs, f_wb_wr_nacks,
+ f_wb_wr_outstanding, f_wb_rd_nreqs, f_wb_rd_nacks,
+ f_wb_rd_outstanding };
+ // Verilator lint_on UNUSED
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/axlite_wrapper.vhd b/rtl/wb2axip/axlite_wrapper.vhd
new file mode 100644
index 0000000..591d56d
--- /dev/null
+++ b/rtl/wb2axip/axlite_wrapper.vhd
@@ -0,0 +1,136 @@
+--------------------------------------------------------------------------------
+--
+-- Filename: axlite_wrapper.vhd
+--
+-- Project: WB2AXIPSP: bus bridges and other odds and ends
+--
+-- Purpose: When wrapped with this wrapper, the axlite2wbsp.v core was
+-- verified to work in FPGA silicon via Vivado.
+--
+-- Thank you Ambroz for donating this code!
+--
+-- Creator: Ambroz Bizjak
+--
+--------------------------------------------------------------------------------
+--
+-- Copyright (C) 2019-2023, Gisselquist Technology, LLC
+--
+-- This file is part of the WB2AXIP project.
+--
+-- The WB2AXIP project contains free software and gateware, licensed under the
+-- Apache License, Version 2.0 (the "License"). You may not use this project,
+-- or this file, except in compliance with the License. You may obtain a copy
+-- of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the 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.
+--
+--------------------------------------------------------------------------------
+--
+--
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+
+entity axlite2wbsp_wrapper is
+ generic (
+ C_AXI_ADDR_WIDTH : integer := 28;
+ LGFIFO : integer := 4;
+ F_MAXSTALL : integer := 3;
+ F_MAXDELAY : integer := 3;
+
+ --- Must not be changed.
+ C_AXI_DATA_WIDTH : integer := 32
+ );
+ port (
+ s_axi_aclk : in std_logic;
+ s_axi_aresetn : in std_logic;
+
+ s_axi_awready : out std_logic;
+ s_axi_awaddr : in std_logic_vector(C_AXI_ADDR_WIDTH-1 downto 0);
+ s_axi_awcache : in std_logic_vector(3 downto 0);
+ s_axi_awprot : in std_logic_vector(2 downto 0);
+ s_axi_awvalid : in std_logic;
+ s_axi_wready : out std_logic;
+ s_axi_wdata : in std_logic_vector(C_AXI_DATA_WIDTH-1 downto 0);
+ s_axi_wstrb : in std_logic_vector(C_AXI_DATA_WIDTH/8-1 downto 0);
+ s_axi_wvalid : in std_logic;
+ s_axi_bresp : out std_logic_vector(1 downto 0);
+ s_axi_bvalid : out std_logic;
+ s_axi_bready : in std_logic;
+ s_axi_arready : out std_logic;
+ s_axi_araddr : in std_logic_vector(C_AXI_ADDR_WIDTH-1 downto 0);
+ s_axi_arcache : in std_logic_vector(3 downto 0);
+ s_axi_arprot : in std_logic_vector(2 downto 0);
+ s_axi_arvalid : in std_logic;
+ s_axi_rresp : out std_logic_vector(1 downto 0);
+ s_axi_rvalid : out std_logic;
+ s_axi_rdata : out std_logic_vector(C_AXI_DATA_WIDTH-1 downto 0);
+ s_axi_rready : in std_logic;
+
+ m_wb_reset : out std_logic;
+ m_wb_cyc : out std_logic;
+ m_wb_stb : out std_logic;
+ m_wb_we : out std_logic;
+ m_wb_adr : out std_logic_vector(C_AXI_ADDR_WIDTH-2-1 downto 0);
+ m_wb_dat_w : out std_logic_vector(C_AXI_DATA_WIDTH-1 downto 0);
+ m_wb_sel : out std_logic_vector(C_AXI_DATA_WIDTH/8-1 downto 0);
+ m_wb_ack : in std_logic;
+ m_wb_stall : in std_logic;
+ m_wb_dat_r : in std_logic_vector(C_AXI_DATA_WIDTH-1 downto 0);
+ m_wb_err : in std_logic
+ );
+end axlite2wbsp_wrapper;
+
+architecture Behavioral of axlite2wbsp_wrapper is
+
+begin
+ axlite2wbsp : entity work.axlite2wbsp
+ generic map (
+ C_AXI_ADDR_WIDTH => C_AXI_ADDR_WIDTH,
+ LGFIFO => LGFIFO,
+ F_MAXSTALL => F_MAXSTALL,
+ F_MAXDELAY => F_MAXDELAY
+ )
+ port map (
+ i_clk => s_axi_aclk,
+ i_axi_reset_n => s_axi_aresetn,
+ o_axi_awready => s_axi_awready,
+ i_axi_awaddr => s_axi_awaddr,
+ i_axi_awcache => s_axi_awcache,
+ i_axi_awprot => s_axi_awprot,
+ i_axi_awvalid => s_axi_awvalid,
+ o_axi_wready => s_axi_wready,
+ i_axi_wdata => s_axi_wdata,
+ i_axi_wstrb => s_axi_wstrb,
+ i_axi_wvalid => s_axi_wvalid,
+ o_axi_bresp => s_axi_bresp,
+ o_axi_bvalid => s_axi_bvalid,
+ i_axi_bready => s_axi_bready,
+ o_axi_arready => s_axi_arready,
+ i_axi_araddr => s_axi_araddr,
+ i_axi_arcache => s_axi_arcache,
+ i_axi_arprot => s_axi_arprot,
+ i_axi_arvalid => s_axi_arvalid,
+ o_axi_rresp => s_axi_rresp,
+ o_axi_rvalid => s_axi_rvalid,
+ o_axi_rdata => s_axi_rdata,
+ i_axi_rready => s_axi_rready,
+ o_reset => m_wb_reset,
+ o_wb_cyc => m_wb_cyc,
+ o_wb_stb => m_wb_stb,
+ o_wb_we => m_wb_we,
+ o_wb_addr => m_wb_adr,
+ o_wb_data => m_wb_dat_w,
+ o_wb_sel => m_wb_sel,
+ i_wb_ack => m_wb_ack,
+ i_wb_stall => m_wb_stall,
+ i_wb_data => m_wb_dat_r,
+ i_wb_err => m_wb_err
+ );
+
+end Behavioral;
diff --git a/rtl/wb2axip/demoaxi.v b/rtl/wb2axip/demoaxi.v
new file mode 100644
index 0000000..1babb05
--- /dev/null
+++ b/rtl/wb2axip/demoaxi.v
@@ -0,0 +1,720 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: demoaxi.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Demonstrate an AXI-lite bus design. The goal of this design
+// is to support a completely pipelined AXI-lite transaction
+// which can transfer one data item per clock.
+//
+// Note that the AXI spec requires that there be no combinatorial
+// logic between input ports and output ports. Hence all of the *valid
+// and *ready signals produced here are registered. This forces us into
+// the buffered handshake strategy.
+//
+// Some curious variable meanings below:
+//
+// !axi_arvalid is synonymous with having a request, but stalling because
+// of a current request sitting in axi_rvalid with !axi_rready
+// !axi_awvalid is also synonymous with having an axi address being
+// received, but either the axi_bvalid && !axi_bready, or
+// no write data has been received
+// !axi_wvalid is similar to axi_awvalid.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2018-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+//
+`timescale 1 ns / 1 ps
+// }}}
+module demoaxi #(
+ // {{{
+ // Users to add parameters here
+ parameter [0:0] OPT_READ_SIDEEFFECTS = 1,
+ // User parameters ends
+ // Do not modify the parameters beyond this line
+ // Width of S_AXI data bus
+ parameter integer C_S_AXI_DATA_WIDTH = 32,
+ // Width of S_AXI address bus
+ parameter integer C_S_AXI_ADDR_WIDTH = 8
+ // }}}
+ ) (
+ // {{{
+ // Users to add ports here
+ // No user ports (yet) in this design
+ // User ports ends
+
+ // Do not modify the ports beyond this line
+ // Global Clock Signal
+ input wire S_AXI_ACLK,
+ // Global Reset Signal. This Signal is Active LOW
+ input wire S_AXI_ARESETN,
+ // Write address (issued by master, acceped by Slave)
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
+ // Write channel Protection type. This signal indicates the
+ // privilege and security level of the transaction, and whether
+ // the transaction is a data access or an instruction access.
+ input wire [2 : 0] S_AXI_AWPROT,
+ // Write address valid. This signal indicates that the master
+ // signaling valid write address and control information.
+ input wire S_AXI_AWVALID,
+ // Write address ready. This signal indicates that the slave
+ // is ready to accept an address and associated control signals.
+ output wire S_AXI_AWREADY,
+ // Write data (issued by master, acceped by Slave)
+ input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
+ // Write strobes. This signal indicates which byte lanes hold
+ // valid data. There is one write strobe bit for each eight
+ // bits of the write data bus.
+ input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
+ // Write valid. This signal indicates that valid write
+ // data and strobes are available.
+ input wire S_AXI_WVALID,
+ // Write ready. This signal indicates that the slave
+ // can accept the write data.
+ output wire S_AXI_WREADY,
+ // Write response. This signal indicates the status
+ // of the write transaction.
+ output wire [1 : 0] S_AXI_BRESP,
+ // Write response valid. This signal indicates that the channel
+ // is signaling a valid write response.
+ output wire S_AXI_BVALID,
+ // Response ready. This signal indicates that the master
+ // can accept a write response.
+ input wire S_AXI_BREADY,
+ // Read address (issued by master, acceped by Slave)
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
+ // Protection type. This signal indicates the privilege
+ // and security level of the transaction, and whether the
+ // transaction is a data access or an instruction access.
+ input wire [2 : 0] S_AXI_ARPROT,
+ // Read address valid. This signal indicates that the channel
+ // is signaling valid read address and control information.
+ input wire S_AXI_ARVALID,
+ // Read address ready. This signal indicates that the slave is
+ // ready to accept an address and associated control signals.
+ output wire S_AXI_ARREADY,
+ // Read data (issued by slave)
+ output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
+ // Read response. This signal indicates the status of the
+ // read transfer.
+ output wire [1 : 0] S_AXI_RRESP,
+ // Read valid. This signal indicates that the channel is
+ // signaling the required read data.
+ output wire S_AXI_RVALID,
+ // Read ready. This signal indicates that the master can
+ // accept the read data and response information.
+ input wire S_AXI_RREADY
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ // AXI4LITE signals
+ reg axi_awready;
+ reg axi_wready;
+ reg axi_bvalid;
+ reg axi_arready;
+ reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
+ reg axi_rvalid;
+
+ // Example-specific design signals
+ // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
+ // ADDR_LSB is used for addressing 32/64 bit registers/memories
+ // ADDR_LSB = 2 for 32 bits (n downto 2)
+ // ADDR_LSB = 3 for 64 bits (n downto 3)
+ localparam integer ADDR_LSB = 2;
+ localparam integer AW = C_S_AXI_ADDR_WIDTH-2;
+ localparam integer DW = C_S_AXI_DATA_WIDTH;
+ //----------------------------------------------
+ //-- Signals for user logic register space example
+ //------------------------------------------------
+ reg [DW-1:0] slv_mem [0:63];
+
+ // I/O Connections assignments
+
+ assign S_AXI_AWREADY = axi_awready;
+ assign S_AXI_WREADY = axi_wready;
+ assign S_AXI_BRESP = 2'b00; // The OKAY response
+ assign S_AXI_BVALID = axi_bvalid;
+ assign S_AXI_ARREADY = axi_arready;
+ assign S_AXI_RDATA = axi_rdata;
+ assign S_AXI_RRESP = 2'b00; // The OKAY response
+ assign S_AXI_RVALID = axi_rvalid;
+ // Implement axi_*wready generation
+ // }}}
+ //////////////////////////////////////
+ //
+ // Read processing
+ //
+ //
+ wire valid_read_request,
+ read_response_stall;
+
+ assign valid_read_request = S_AXI_ARVALID || !S_AXI_ARREADY;
+ assign read_response_stall = S_AXI_RVALID && !S_AXI_RREADY;
+
+ //
+ // The read response channel valid signal
+ //
+ initial axi_rvalid = 1'b0;
+ always @(posedge S_AXI_ACLK )
+ if (!S_AXI_ARESETN)
+ axi_rvalid <= 0;
+ else if (read_response_stall)
+ // Need to stay valid as long as the return path is stalled
+ axi_rvalid <= 1'b1;
+ else if (valid_read_request)
+ axi_rvalid <= 1'b1;
+ else
+ // Any stall has cleared, so we can always
+ // clear the valid signal in this case
+ axi_rvalid <= 1'b0;
+
+ reg [C_S_AXI_ADDR_WIDTH-1 : 0] pre_raddr, rd_addr;
+
+ // Buffer the address
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARREADY)
+ pre_raddr <= S_AXI_ARADDR;
+
+ always @(*)
+ if (!axi_arready)
+ rd_addr = pre_raddr;
+ else
+ rd_addr = S_AXI_ARADDR;
+
+ //
+ // Read the data
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!read_response_stall
+ &&(!OPT_READ_SIDEEFFECTS || valid_read_request))
+ // If the outgoing channel is not stalled (above)
+ // then read
+ axi_rdata <= slv_mem[rd_addr[AW+ADDR_LSB-1:ADDR_LSB]];
+
+ //
+ // The read address channel ready signal
+ //
+ initial axi_arready = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arready <= 1'b1;
+ else if (read_response_stall)
+ begin
+ // Outgoing channel is stalled
+ // As long as something is already in the buffer,
+ // axi_arready needs to stay low
+ axi_arready <= !valid_read_request;
+ end else
+ axi_arready <= 1'b1;
+
+ //////////////////////////////////////
+ //
+ // Write processing
+ //
+ //
+ reg [C_S_AXI_ADDR_WIDTH-1 : 0] pre_waddr, waddr;
+ reg [C_S_AXI_DATA_WIDTH-1 : 0] pre_wdata, wdata;
+ reg [(C_S_AXI_DATA_WIDTH/8)-1 : 0] pre_wstrb, wstrb;
+
+ wire valid_write_address, valid_write_data,
+ write_response_stall;
+
+ assign valid_write_address = S_AXI_AWVALID || !axi_awready;
+ assign valid_write_data = S_AXI_WVALID || !axi_wready;
+ assign write_response_stall= S_AXI_BVALID && !S_AXI_BREADY;
+
+ //
+ // The write address channel ready signal
+ //
+ initial axi_awready = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_awready <= 1'b1;
+ else if (write_response_stall)
+ begin
+ // The output channel is stalled
+ // If our buffer is full, we need to remain stalled
+ // Likewise if it is empty, and there's a request,
+ // we'll need to stall.
+ axi_awready <= !valid_write_address;
+ end else if (valid_write_data)
+ // The output channel is clear, and write data
+ // are available
+ axi_awready <= 1'b1;
+ else
+ // If we were ready before, then remain ready unless an
+ // address unaccompanied by data shows up
+ axi_awready <= ((axi_awready)&&(!S_AXI_AWVALID));
+ // This is equivalent to
+ // axi_awready <= !valid_write_address
+
+ //
+ // The write data channel ready signal
+ //
+ initial axi_wready = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_wready <= 1'b1;
+ else if (write_response_stall)
+ // The output channel is stalled
+ // We can remain ready until valid
+ // write data shows up
+ axi_wready <= !valid_write_data;
+ else if (valid_write_address)
+ // The output channel is clear, and a write address
+ // is available
+ axi_wready <= 1'b1;
+ else
+ // if we were ready before, and there's no new data avaialble
+ // to cause us to stall, remain ready
+ axi_wready <= (axi_wready)&&(!S_AXI_WVALID);
+ // This is equivalent to
+ // axi_wready <= !valid_write_data
+
+
+ // Buffer the address
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_AWREADY)
+ pre_waddr <= S_AXI_AWADDR;
+
+ // Buffer the data
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_WREADY)
+ begin
+ pre_wdata <= S_AXI_WDATA;
+ pre_wstrb <= S_AXI_WSTRB;
+ end
+
+ always @(*)
+ if (!axi_awready)
+ // Read the write address from our "buffer"
+ waddr = pre_waddr;
+ else
+ waddr = S_AXI_AWADDR;
+
+ always @(*)
+ if (!axi_wready)
+ begin
+ // Read the write data from our "buffer"
+ wstrb = pre_wstrb;
+ wdata = pre_wdata;
+ end else begin
+ wstrb = S_AXI_WSTRB;
+ wdata = S_AXI_WDATA;
+ end
+
+ //
+ // Actually (finally) write the data
+ //
+ always @(posedge S_AXI_ACLK )
+ // If the output channel isn't stalled, and
+ if (!write_response_stall
+ // If we have a valid address, and
+ && valid_write_address
+ // If we have valid data
+ && valid_write_data)
+ begin
+ if (wstrb[0])
+ slv_mem[waddr[AW+ADDR_LSB-1:ADDR_LSB]][7:0]
+ <= wdata[7:0];
+ if (wstrb[1])
+ slv_mem[waddr[AW+ADDR_LSB-1:ADDR_LSB]][15:8]
+ <= wdata[15:8];
+ if (wstrb[2])
+ slv_mem[waddr[AW+ADDR_LSB-1:ADDR_LSB]][23:16]
+ <= wdata[23:16];
+ if (wstrb[3])
+ slv_mem[waddr[AW+ADDR_LSB-1:ADDR_LSB]][31:24]
+ <= wdata[31:24];
+ end
+
+ //
+ // The write response channel valid signal
+ //
+ initial axi_bvalid = 1'b0;
+ always @(posedge S_AXI_ACLK )
+ if (!S_AXI_ARESETN)
+ axi_bvalid <= 1'b0;
+ //
+ // The outgoing response channel should indicate a valid write if ...
+ // 1. We have a valid address, and
+ else if (valid_write_address
+ // 2. We had valid data
+ && valid_write_data)
+ // It doesn't matter here if we are stalled or not
+ // We can keep setting ready as often as we want
+ axi_bvalid <= 1'b1;
+ else if (S_AXI_BREADY)
+ // Otherwise, if BREADY was true, then it was just accepted
+ // and can return to idle now
+ axi_bvalid <= 1'b0;
+
+ // Make Verilator happy
+ // Verilator lint_off UNUSED
+ wire [4*ADDR_LSB+5:0] unused;
+ assign unused = { S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_AWADDR[ADDR_LSB-1:0],
+ rd_addr[ADDR_LSB-1:0],
+ waddr[ADDR_LSB-1:0],
+ S_AXI_ARADDR[ADDR_LSB-1:0] };
+ // Verilator lint_on UNUSED
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam F_LGDEPTH = 4;
+
+ reg f_past_valid;
+ wire [(F_LGDEPTH-1):0] f_axi_awr_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_rd_outstanding;
+
+ faxil_slave #(// .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH),
+ // .F_OPT_NO_READS(1'b0),
+ // .F_OPT_NO_WRITES(1'b0),
+ .F_OPT_XILINX(1),
+ .F_LGDEPTH(F_LGDEPTH))
+ properties (
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awprot(S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp(S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arprot(S_AXI_ARPROT),
+ //
+ .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(f_axi_rd_outstanding),
+ .f_axi_wr_outstanding(f_axi_wr_outstanding),
+ .f_axi_awr_outstanding(f_axi_awr_outstanding));
+
+ initial f_past_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1'b1;
+
+ ///////
+ //
+ // Properties necessary to pass induction
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (!S_AXI_RVALID)
+ assert(f_axi_rd_outstanding == 0);
+ else if (!S_AXI_ARREADY)
+ assert((f_axi_rd_outstanding == 2)||(f_axi_rd_outstanding == 1));
+ else
+ assert(f_axi_rd_outstanding == 1);
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ if (axi_bvalid)
+ begin
+ assert(f_axi_awr_outstanding == 1+(axi_awready ? 0:1));
+ assert(f_axi_wr_outstanding == 1+(axi_wready ? 0:1));
+ end else begin
+ assert(f_axi_awr_outstanding == (axi_awready ? 0:1));
+ assert(f_axi_wr_outstanding == (axi_wready ? 0:1));
+ end
+ end
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ // In addition to making sure the design returns a value, any value,
+ // let's cover returning three values on adjacent clocks--just to prove
+ // we can.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK )
+ if ((f_past_valid)&&(S_AXI_ARESETN))
+ cover(($past((S_AXI_BVALID && S_AXI_BREADY)))
+ &&($past((S_AXI_BVALID && S_AXI_BREADY),2))
+ &&(S_AXI_BVALID && S_AXI_BREADY));
+
+ always @(posedge S_AXI_ACLK )
+ if ((f_past_valid)&&(S_AXI_ARESETN))
+ cover(($past((S_AXI_RVALID && S_AXI_RREADY)))
+ &&($past((S_AXI_RVALID && S_AXI_RREADY),2))
+ &&(S_AXI_RVALID && S_AXI_RREADY));
+
+ // Let's go just one further, and verify we can do three returns in a
+ // row. Why? It might just be possible that one value was waiting
+ // already, and so we haven't yet tested that two requests could be
+ // made in a row.
+ always @(posedge S_AXI_ACLK )
+ if ((f_past_valid)&&(S_AXI_ARESETN))
+ cover(($past((S_AXI_BVALID && S_AXI_BREADY)))
+ &&($past((S_AXI_BVALID && S_AXI_BREADY),2))
+ &&($past((S_AXI_BVALID && S_AXI_BREADY),3))
+ &&(S_AXI_BVALID && S_AXI_BREADY));
+
+ always @(posedge S_AXI_ACLK )
+ if ((f_past_valid)&&(S_AXI_ARESETN))
+ cover(($past((S_AXI_RVALID && S_AXI_RREADY)))
+ &&($past((S_AXI_RVALID && S_AXI_RREADY),2))
+ &&($past((S_AXI_RVALID && S_AXI_RREADY),3))
+ &&(S_AXI_RVALID && S_AXI_RREADY));
+
+ //
+ // Let's create a sophisticated cover statement designed to show off
+ // how our core can handle stalls and non-valids, synchronizing
+ // across multiple scenarios
+ reg [22:0] fw_wrdemo_pipe, fr_wrdemo_pipe;
+ always @(*)
+ if (!S_AXI_ARESETN)
+ fw_wrdemo_pipe = 0;
+ else begin
+ fw_wrdemo_pipe[0] = (S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[1] = fr_wrdemo_pipe[0]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[2] = fr_wrdemo_pipe[1]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ //
+ //
+ fw_wrdemo_pipe[3] = fr_wrdemo_pipe[2]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[4] = fr_wrdemo_pipe[3]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[5] = fr_wrdemo_pipe[4]
+ &&(!S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[6] = fr_wrdemo_pipe[5]
+ &&(S_AXI_AWVALID)
+ &&( S_AXI_WVALID)
+ &&( S_AXI_BREADY);
+ fw_wrdemo_pipe[7] = fr_wrdemo_pipe[6]
+ &&(!S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&( S_AXI_BREADY);
+ fw_wrdemo_pipe[8] = fr_wrdemo_pipe[7]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[9] = fr_wrdemo_pipe[8]
+// &&(S_AXI_AWVALID)
+// &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[10] = fr_wrdemo_pipe[9]
+// &&(S_AXI_AWVALID)
+// &&(S_AXI_WVALID)
+ // &&(S_AXI_BREADY);
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[11] = fr_wrdemo_pipe[10]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(!S_AXI_BREADY);
+ fw_wrdemo_pipe[12] = fr_wrdemo_pipe[11]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[13] = fr_wrdemo_pipe[12]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[14] = fr_wrdemo_pipe[13]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(f_axi_awr_outstanding == 0)
+ &&(f_axi_wr_outstanding == 0)
+ &&(S_AXI_BREADY);
+ //
+ //
+ //
+ fw_wrdemo_pipe[15] = fr_wrdemo_pipe[14]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[16] = fr_wrdemo_pipe[15]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[17] = fr_wrdemo_pipe[16]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[18] = fr_wrdemo_pipe[17]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(!S_AXI_BREADY);
+ fw_wrdemo_pipe[19] = fr_wrdemo_pipe[18]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[20] = fr_wrdemo_pipe[19]
+ &&(S_AXI_AWVALID)
+ &&(S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[21] = fr_wrdemo_pipe[20]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ fw_wrdemo_pipe[22] = fr_wrdemo_pipe[21]
+ &&(!S_AXI_AWVALID)
+ &&(!S_AXI_WVALID)
+ &&(S_AXI_BREADY);
+ end
+
+ always @(posedge S_AXI_ACLK)
+ fr_wrdemo_pipe <= fw_wrdemo_pipe;
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ begin
+ cover(fw_wrdemo_pipe[0]);
+ cover(fw_wrdemo_pipe[1]);
+ cover(fw_wrdemo_pipe[2]);
+ cover(fw_wrdemo_pipe[3]);
+ cover(fw_wrdemo_pipe[4]);
+ cover(fw_wrdemo_pipe[5]);
+ cover(fw_wrdemo_pipe[6]);
+ cover(fw_wrdemo_pipe[7]); //
+ cover(fw_wrdemo_pipe[8]);
+ cover(fw_wrdemo_pipe[9]);
+ cover(fw_wrdemo_pipe[10]);
+ cover(fw_wrdemo_pipe[11]);
+ cover(fw_wrdemo_pipe[12]);
+ cover(fw_wrdemo_pipe[13]);
+ cover(fw_wrdemo_pipe[14]);
+ cover(fw_wrdemo_pipe[15]);
+ cover(fw_wrdemo_pipe[16]);
+ cover(fw_wrdemo_pipe[17]);
+ cover(fw_wrdemo_pipe[18]);
+ cover(fw_wrdemo_pipe[19]);
+ cover(fw_wrdemo_pipe[20]);
+ cover(fw_wrdemo_pipe[21]);
+ cover(fw_wrdemo_pipe[22]);
+ end
+
+ //
+ // Now let's repeat, but for a read demo
+ reg [10:0] fw_rddemo_pipe, fr_rddemo_pipe;
+ always @(*)
+ if (!S_AXI_ARESETN)
+ fw_rddemo_pipe = 0;
+ else begin
+ fw_rddemo_pipe[0] = (S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[1] = fr_rddemo_pipe[0]
+ &&(!S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[2] = fr_rddemo_pipe[1]
+ &&(!S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ //
+ //
+ fw_rddemo_pipe[3] = fr_rddemo_pipe[2]
+ &&(S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[4] = fr_rddemo_pipe[3]
+ &&(S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[5] = fr_rddemo_pipe[4]
+ &&(S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[6] = fr_rddemo_pipe[5]
+ &&(S_AXI_ARVALID)
+ &&(!S_AXI_RREADY);
+ fw_rddemo_pipe[7] = fr_rddemo_pipe[6]
+ &&(S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[8] = fr_rddemo_pipe[7]
+ &&(S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[9] = fr_rddemo_pipe[8]
+ &&(!S_AXI_ARVALID)
+ &&(S_AXI_RREADY);
+ fw_rddemo_pipe[10] = fr_rddemo_pipe[9]
+ &&(f_axi_rd_outstanding == 0);
+ end
+
+ initial fr_rddemo_pipe = 0;
+ always @(posedge S_AXI_ACLK)
+ fr_rddemo_pipe <= fw_rddemo_pipe;
+
+ always @(*)
+ begin
+ cover(fw_rddemo_pipe[0]);
+ cover(fw_rddemo_pipe[1]);
+ cover(fw_rddemo_pipe[2]);
+ cover(fw_rddemo_pipe[3]);
+ cover(fw_rddemo_pipe[4]);
+ cover(fw_rddemo_pipe[5]);
+ cover(fw_rddemo_pipe[6]);
+ cover(fw_rddemo_pipe[7]);
+ cover(fw_rddemo_pipe[8]);
+ cover(fw_rddemo_pipe[9]);
+ cover(fw_rddemo_pipe[10]);
+ end
+`endif
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/demofull.v b/rtl/wb2axip/demofull.v
new file mode 100644
index 0000000..5d0c604
--- /dev/null
+++ b/rtl/wb2axip/demofull.v
@@ -0,0 +1,1285 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: demofull.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Demonstrate a formally verified AXI4 core with a (basic)
+// interface. This interface is explained below.
+//
+// Performance: This core has been designed for a total throughput of one beat
+// per clock cycle. Both read and write channels can achieve
+// this. The write channel will also introduce two clocks of latency,
+// assuming no other latency from the master. This means it will take
+// a minimum of 3+AWLEN clock cycles per transaction of (1+AWLEN) beats,
+// including both address and acknowledgment cycles. The read channel
+// will introduce a single clock of latency, requiring 2+ARLEN cycles
+// per transaction of 1+ARLEN beats.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module demofull #(
+ // {{{
+ parameter integer C_S_AXI_ID_WIDTH = 2,
+ parameter integer C_S_AXI_DATA_WIDTH = 32,
+ parameter integer C_S_AXI_ADDR_WIDTH = 6,
+ parameter [0:0] OPT_LOCK = 1'b0,
+ parameter [0:0] OPT_LOCKID = 1'b1,
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ // Some useful short-hand definitions
+ localparam LSB = $clog2(C_S_AXI_DATA_WIDTH)-3
+ // }}}
+ ) (
+ // {{{
+ // User ports
+ // {{{
+ // A very basic protocol-independent peripheral interface
+ // 1. A value will be written any time o_we is true
+ // 2. A value will be read any time o_rd is true
+ // 3. Such a slave might just as easily be written as:
+ //
+ // always @(posedge S_AXI_ACLK)
+ // if (o_we)
+ // begin
+ // for(k=0; k<C_S_AXI_DATA_WIDTH/8; k=k+1)
+ // begin
+ // if (o_wstrb[k])
+ // mem[o_waddr[AW-1:LSB]][k*8+:8] <= o_wdata[k*8+:8]
+ // end
+ // end
+ //
+ // always @(posedge S_AXI_ACLK)
+ // if (o_rd)
+ // i_rdata <= mem[o_raddr[AW-1:LSB]];
+ //
+ // 4. The rule on the input is that i_rdata must be registered,
+ // and that it must only change if o_rd is true. Violating
+ // this rule will cause this core to violate the AXI
+ // protocol standard, as this value is not registered within
+ // this core
+ output reg o_we,
+ output reg [C_S_AXI_ADDR_WIDTH-LSB-1:0] o_waddr,
+ output reg [C_S_AXI_DATA_WIDTH-1:0] o_wdata,
+ output reg [C_S_AXI_DATA_WIDTH/8-1:0] o_wstrb,
+ //
+ output reg o_rd,
+ output reg [C_S_AXI_ADDR_WIDTH-LSB-1:0] o_raddr,
+ input wire [C_S_AXI_DATA_WIDTH-1:0] i_rdata,
+ //
+ // User ports ends
+ // }}}
+ // Do not modify the ports beyond this line
+ // AXI signals
+ // {{{
+ // Global Clock Signal
+ input wire S_AXI_ACLK,
+ // Global Reset Signal. This Signal is Active LOW
+ input wire S_AXI_ARESETN,
+
+ // Write address channel
+ // {{{
+ // Write Address ID
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
+ // Write address
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
+ // Burst length. The burst length gives the exact number of
+ // transfers in a burst
+ input wire [7 : 0] S_AXI_AWLEN,
+ // Burst size. This signal indicates the size of each transfer
+ // in the burst
+ input wire [2 : 0] S_AXI_AWSIZE,
+ // Burst type. The burst type and the size information,
+ // determine how the address for each transfer within the burst
+ // is calculated.
+ input wire [1 : 0] S_AXI_AWBURST,
+ // Lock type. Provides additional information about the
+ // atomic characteristics of the transfer.
+ input wire S_AXI_AWLOCK,
+ // Memory type. This signal indicates how transactions
+ // are required to progress through a system.
+ input wire [3 : 0] S_AXI_AWCACHE,
+ // Protection type. This signal indicates the privilege
+ // and security level of the transaction, and whether
+ // the transaction is a data access or an instruction access.
+ input wire [2 : 0] S_AXI_AWPROT,
+ // Quality of Service, QoS identifier sent for each
+ // write transaction.
+ input wire [3 : 0] S_AXI_AWQOS,
+ // Region identifier. Permits a single physical interface
+ // on a slave to be used for multiple logical interfaces.
+ // Write address valid. This signal indicates that
+ // the channel is signaling valid write address and
+ // control information.
+ input wire S_AXI_AWVALID,
+ // Write address ready. This signal indicates that
+ // the slave is ready to accept an address and associated
+ // control signals.
+ output wire S_AXI_AWREADY,
+ // }}}
+ // Write data channel
+ // {{{
+ // Write Data
+ input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
+ // Write strobes. This signal indicates which byte
+ // lanes hold valid data. There is one write strobe
+ // bit for each eight bits of the write data bus.
+ input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
+ // Write last. This signal indicates the last transfer
+ // in a write burst.
+ input wire S_AXI_WLAST,
+ // Optional User-defined signal in the write data channel.
+ // Write valid. This signal indicates that valid write
+ // data and strobes are available.
+ input wire S_AXI_WVALID,
+ // Write ready. This signal indicates that the slave
+ // can accept the write data.
+ output wire S_AXI_WREADY,
+ // }}}
+ // Write response channel
+ // {{{
+ // Response ID tag. This signal is the ID tag of the
+ // write response.
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
+ // Write response. This signal indicates the status
+ // of the write transaction.
+ output wire [1 : 0] S_AXI_BRESP,
+ // Optional User-defined signal in the write response channel.
+ // Write response valid. This signal indicates that the
+ // channel is signaling a valid write response.
+ output wire S_AXI_BVALID,
+ // Response ready. This signal indicates that the master
+ // can accept a write response.
+ input wire S_AXI_BREADY,
+ // }}}
+ // Read address channel
+ // {{{
+ // Read address ID. This signal is the identification
+ // tag for the read address group of signals.
+ input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
+ // Read address. This signal indicates the initial
+ // address of a read burst transaction.
+ input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
+ // Burst length. The burst length gives the exact number of
+ // transfers in a burst
+ input wire [7 : 0] S_AXI_ARLEN,
+ // Burst size. This signal indicates the size of each transfer
+ // in the burst
+ input wire [2 : 0] S_AXI_ARSIZE,
+ // Burst type. The burst type and the size information,
+ // determine how the address for each transfer within the
+ // burst is calculated.
+ input wire [1 : 0] S_AXI_ARBURST,
+ // Lock type. Provides additional information about the
+ // atomic characteristics of the transfer.
+ input wire S_AXI_ARLOCK,
+ // Memory type. This signal indicates how transactions
+ // are required to progress through a system.
+ input wire [3 : 0] S_AXI_ARCACHE,
+ // Protection type. This signal indicates the privilege
+ // and security level of the transaction, and whether
+ // the transaction is a data access or an instruction access.
+ input wire [2 : 0] S_AXI_ARPROT,
+ // Quality of Service, QoS identifier sent for each
+ // read transaction.
+ input wire [3 : 0] S_AXI_ARQOS,
+ // Region identifier. Permits a single physical interface
+ // on a slave to be used for multiple logical interfaces.
+ // Optional User-defined signal in the read address channel.
+ // Write address valid. This signal indicates that
+ // the channel is signaling valid read address and
+ // control information.
+ input wire S_AXI_ARVALID,
+ // Read address ready. This signal indicates that
+ // the slave is ready to accept an address and associated
+ // control signals.
+ output wire S_AXI_ARREADY,
+ // }}}
+ // Read data (return) channel
+ // {{{
+ // Read ID tag. This signal is the identification tag
+ // for the read data group of signals generated by the slave.
+ output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
+ // Read Data
+ output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
+ // Read response. This signal indicates the status of
+ // the read transfer.
+ output wire [1 : 0] S_AXI_RRESP,
+ // Read last. This signal indicates the last transfer
+ // in a read burst.
+ output wire S_AXI_RLAST,
+ // Optional User-defined signal in the read address channel.
+ // Read valid. This signal indicates that the channel
+ // is signaling the required read data.
+ output wire S_AXI_RVALID,
+ // Read ready. This signal indicates that the master can
+ // accept the read data and response information.
+ input wire S_AXI_RREADY
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ // More useful shorthand definitions
+ localparam AW = C_S_AXI_ADDR_WIDTH;
+ localparam DW = C_S_AXI_DATA_WIDTH;
+ localparam IW = C_S_AXI_ID_WIDTH;
+ // Double buffer the write response channel only
+ reg [IW-1 : 0] r_bid;
+ reg r_bvalid;
+ reg [IW-1 : 0] axi_bid;
+ reg axi_bvalid;
+
+ reg axi_awready, axi_wready;
+ reg [AW-1:0] waddr;
+ wire [AW-1:0] next_wr_addr;
+
+ // Vivado will warn about wlen only using 4-bits. This is
+ // to be expected, since the axi_addr module only needs to use
+ // the bottom four bits of wlen to determine address increments
+ reg [7:0] wlen;
+ // Vivado will also warn about the top bit of wsize being unused.
+ // This is also to be expected for a DATA_WIDTH of 32-bits.
+ reg [2:0] wsize;
+ reg [1:0] wburst;
+
+ wire m_awvalid, m_awlock;
+ reg m_awready;
+ wire [AW-1:0] m_awaddr;
+ wire [1:0] m_awburst;
+ wire [2:0] m_awsize;
+ wire [7:0] m_awlen;
+ wire [IW-1:0] m_awid;
+
+ wire [AW-1:0] next_rd_addr;
+
+ // Vivado will warn about rlen only using 4-bits. This is
+ // to be expected, since for a DATA_WIDTH of 32-bits, the axi_addr
+ // module only uses the bottom four bits of rlen to determine
+ // address increments
+ reg [7:0] rlen;
+ // Vivado will also warn about the top bit of wsize being unused.
+ // This is also to be expected for a DATA_WIDTH of 32-bits.
+ reg [2:0] rsize;
+ reg [1:0] rburst;
+ reg [IW-1:0] rid;
+ reg rlock;
+ reg axi_arready;
+ reg [8:0] axi_rlen;
+ reg [AW-1:0] raddr;
+
+ // Read skid buffer
+ reg rskd_valid, rskd_last, rskd_lock;
+ wire rskd_ready;
+ reg [IW-1:0] rskd_id;
+
+ // Exclusive address register checking
+ reg exclusive_write, block_write;
+ wire write_lock_valid;
+ reg axi_exclusive_write;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AW Skid buffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ skidbuffer #(
+ // {{{
+ .DW(AW+2+3+1+8+IW),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(1'b0)
+ // }}}
+ ) awbuf(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data({ S_AXI_AWADDR, S_AXI_AWBURST, S_AXI_AWSIZE,
+ S_AXI_AWLOCK, S_AXI_AWLEN, S_AXI_AWID }),
+ .o_valid(m_awvalid), .i_ready(m_awready),
+ .o_data({ m_awaddr, m_awburst, m_awsize,
+ m_awlock, m_awlen, m_awid })
+ // }}}
+ );
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // axi_awready, axi_wready
+ // {{{
+ initial axi_awready = 1;
+ initial axi_wready = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ axi_awready <= 1;
+ axi_wready <= 0;
+ end else if (m_awvalid && m_awready)
+ begin
+ axi_awready <= 0;
+ axi_wready <= 1;
+ end else if (S_AXI_WVALID && S_AXI_WREADY)
+ begin
+ axi_awready <= (S_AXI_WLAST)&&(!S_AXI_BVALID || S_AXI_BREADY);
+ axi_wready <= (!S_AXI_WLAST);
+ end else if (!axi_awready)
+ begin
+ if (S_AXI_WREADY)
+ axi_awready <= 1'b0;
+ else if (r_bvalid && !S_AXI_BREADY)
+ axi_awready <= 1'b0;
+ else
+ axi_awready <= 1'b1;
+ end
+ // }}}
+
+ // Exclusive write calculation
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_LOCK)
+ begin
+ exclusive_write <= 0;
+ block_write <= 0;
+ end else if (m_awvalid && m_awready)
+ begin
+ exclusive_write <= 1'b0;
+ block_write <= 1'b0;
+ if (write_lock_valid)
+ exclusive_write <= 1'b1;
+ else if (m_awlock)
+ block_write <= 1'b1;
+ end else if (m_awready)
+ begin
+ exclusive_write <= 1'b0;
+ block_write <= 1'b0;
+ end
+
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_LOCK)
+ axi_exclusive_write <= 0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ axi_exclusive_write <= exclusive_write;
+ if (OPT_LOWPOWER && (!S_AXI_WVALID || !S_AXI_WREADY || !S_AXI_WLAST)
+ && !r_bvalid)
+ axi_exclusive_write <= 0;
+ end
+ // }}}
+
+ // Next write address calculation
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (m_awready)
+ begin
+ waddr <= m_awaddr;
+ wburst <= m_awburst;
+ wsize <= m_awsize;
+ wlen <= m_awlen;
+ end else if (S_AXI_WVALID)
+ waddr <= next_wr_addr;
+
+ axi_addr #(
+ // {{{
+ .AW(AW), .DW(DW)
+ // }}}
+ ) get_next_wr_addr(
+ // {{{
+ waddr, wsize, wburst, wlen,
+ next_wr_addr
+ // }}}
+ );
+ // }}}
+
+ // o_w*
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ begin
+ o_we <= (S_AXI_WVALID && S_AXI_WREADY);
+ o_waddr <= waddr[AW-1:LSB];
+ o_wdata <= S_AXI_WDATA;
+ if (block_write)
+ o_wstrb <= 0;
+ else
+ o_wstrb <= S_AXI_WSTRB;
+
+ if (!S_AXI_ARESETN)
+ o_we <= 0;
+ if (OPT_LOWPOWER && (!S_AXI_ARESETN || !S_AXI_WVALID
+ || !S_AXI_WREADY))
+ begin
+ o_waddr <= 0;
+ o_wdata <= 0;
+ o_wstrb <= 0;
+ end
+ end
+ // }}}
+
+ //
+ // Write return path
+ // {{{
+ // r_bvalid
+ // {{{
+ initial r_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_bvalid <= 1'b0;
+ else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST
+ &&(S_AXI_BVALID && !S_AXI_BREADY))
+ r_bvalid <= 1'b1;
+ else if (S_AXI_BREADY)
+ r_bvalid <= 1'b0;
+ // }}}
+
+ // r_bid, axi_bid
+ // {{{
+ initial r_bid = 0;
+ initial axi_bid = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (m_awready && (!OPT_LOWPOWER || m_awvalid))
+ r_bid <= m_awid;
+
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ axi_bid <= r_bid;
+
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ begin
+ r_bid <= 0;
+ axi_bid <= 0;
+ end
+ end
+ // }}}
+
+ // axi_bvalid
+ // {{{
+ initial axi_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_bvalid <= 0;
+ else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
+ axi_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axi_bvalid <= r_bvalid;
+ // }}}
+
+ // m_awready
+ // {{{
+ always @(*)
+ begin
+ m_awready = axi_awready;
+ if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST
+ && (!S_AXI_BVALID || S_AXI_BREADY))
+ m_awready = 1;
+ end
+ // }}}
+
+ // At one time, axi_awready was the same as S_AXI_AWREADY. Now, though,
+ // with the extra write address skid buffer, this is no longer the case.
+ // S_AXI_AWREADY is handled/created/managed by the skid buffer.
+ //
+ // assign S_AXI_AWREADY = axi_awready;
+ //
+ // The rest of these signals can be set according to their registered
+ // values above.
+ assign S_AXI_WREADY = axi_wready;
+ assign S_AXI_BVALID = axi_bvalid;
+ assign S_AXI_BID = axi_bid;
+ //
+ // This core does not produce any bus errors, nor does it support
+ // exclusive access, so 2'b00 will always be the correct response.
+ assign S_AXI_BRESP = { 1'b0, axi_exclusive_write };
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // axi_arready
+ // {{{
+ initial axi_arready = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arready <= 1;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_arready <= (S_AXI_ARLEN==0)&&(o_rd);
+ else if (o_rd)
+ axi_arready <= (axi_rlen <= 1);
+ // }}}
+
+ // axi_rlen
+ // {{{
+ initial axi_rlen = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_rlen <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ axi_rlen <= S_AXI_ARLEN + (o_rd ? 0:1);
+ else if (o_rd)
+ axi_rlen <= axi_rlen - 1;
+ // }}}
+
+ // Next read address calculation
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (o_rd)
+ raddr <= next_rd_addr;
+ else if (S_AXI_ARREADY)
+ begin
+ raddr <= S_AXI_ARADDR;
+ if (OPT_LOWPOWER && !S_AXI_ARVALID)
+ raddr <= 0;
+ end
+
+ // r*
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARREADY)
+ begin
+ rburst <= S_AXI_ARBURST;
+ rsize <= S_AXI_ARSIZE;
+ rlen <= S_AXI_ARLEN;
+ rid <= S_AXI_ARID;
+ rlock <= S_AXI_ARLOCK && S_AXI_ARVALID && OPT_LOCK;
+
+ if (OPT_LOWPOWER && !S_AXI_ARVALID)
+ begin
+ rburst <= 0;
+ rsize <= 0;
+ rlen <= 0;
+ rid <= 0;
+ rlock <= 0;
+ end
+ end
+ // }}}
+
+ axi_addr #(
+ // {{{
+ .AW(AW), .DW(DW)
+ // }}}
+ ) get_next_rd_addr(
+ // {{{
+ (S_AXI_ARREADY ? S_AXI_ARADDR : raddr),
+ (S_AXI_ARREADY ? S_AXI_ARSIZE : rsize),
+ (S_AXI_ARREADY ? S_AXI_ARBURST: rburst),
+ (S_AXI_ARREADY ? S_AXI_ARLEN : rlen),
+ next_rd_addr
+ // }}}
+ );
+ // }}}
+
+ // o_rd, o_raddr
+ // {{{
+ always @(*)
+ begin
+ o_rd = (S_AXI_ARVALID || !S_AXI_ARREADY);
+ if (S_AXI_RVALID && !S_AXI_RREADY)
+ o_rd = 0;
+ if (rskd_valid && !rskd_ready)
+ o_rd = 0;
+ o_raddr = (S_AXI_ARREADY ? S_AXI_ARADDR[AW-1:LSB] : raddr[AW-1:LSB]);
+ end
+ // }}}
+
+ // rskd_valid
+ // {{{
+ initial rskd_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ rskd_valid <= 0;
+ else if (o_rd)
+ rskd_valid <= 1;
+ else if (rskd_ready)
+ rskd_valid <= 0;
+ // }}}
+
+ // rskd_id
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!rskd_valid || rskd_ready)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ rskd_id <= S_AXI_ARID;
+ else
+ rskd_id <= rid;
+ end
+ // }}}
+
+ // rskd_last
+ // {{{
+ initial rskd_last = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!rskd_valid || rskd_ready)
+ begin
+ rskd_last <= 0;
+ if (o_rd && axi_rlen == 1)
+ rskd_last <= 1;
+ if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 0)
+ rskd_last <= 1;
+ end
+ // }}}
+
+ // rskd_lock
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_LOCK)
+ rskd_lock <= 1'b0;
+ else if (!rskd_valid || rskd_ready)
+ begin
+ rskd_lock <= 0;
+ if (!OPT_LOWPOWER || o_rd)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ rskd_lock <= S_AXI_ARLOCK;
+ else
+ rskd_lock <= rlock;
+ end
+ end
+ // }}}
+
+
+ // Outgoing read skidbuffer
+ // {{{
+ skidbuffer #(
+ // {{{
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_OUTREG(1),
+ .DW(IW+2+DW)
+ // }}}
+ ) rskid (
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
+ .i_valid(rskd_valid), .o_ready(rskd_ready),
+ .i_data({ rskd_id, rskd_lock, rskd_last, i_rdata }),
+ .o_valid(S_AXI_RVALID), .i_ready(S_AXI_RREADY),
+ .o_data({ S_AXI_RID, S_AXI_RRESP[0], S_AXI_RLAST,
+ S_AXI_RDATA })
+ // }}}
+ );
+ // }}}
+
+ assign S_AXI_RRESP[1] = 1'b0;
+ assign S_AXI_ARREADY = axi_arready;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Exclusive address caching
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_LOCK && !OPT_LOCKID)
+ begin : EXCLUSIVE_ACCESS_BLOCK
+ // {{{
+ // The AXI4 specification requires that we check one address
+ // per ID. This isn't that. This algorithm checks one ID,
+ // whichever the last ID was. It's designed to be lighter on
+ // the logic requirements, and (unnoticably) not (fully) spec
+ // compliant. (The difference, if noticed at all, will be in
+ // performance when multiple masters try to perform an exclusive
+ // transaction at once.)
+
+ // Local declarations
+ // {{{
+ reg w_valid_lock_request, w_cancel_lock,
+ w_lock_request,
+ lock_valid, returned_lock_valid;
+ reg [AW-LSB-1:0] lock_start, lock_end;
+ reg [3:0] lock_len;
+ reg [1:0] lock_burst;
+ reg [2:0] lock_size;
+ reg [IW-1:0] lock_id;
+ reg w_write_lock_valid;
+ // }}}
+
+ // w_lock_request
+ // {{{
+ always @(*)
+ begin
+ w_lock_request = 0;
+ if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLOCK)
+ w_lock_request = 1;
+ end
+ // }}}
+
+ // w_valid_lock_request
+ // {{{
+ always @(*)
+ begin
+ w_valid_lock_request = 0;
+ if (w_lock_request)
+ w_valid_lock_request = 1;
+ if (o_we && o_waddr == S_AXI_ARADDR[AW-1:LSB])
+ w_valid_lock_request = 0;
+ end
+ // }}}
+
+ // returned_lock_valid
+ // {{{
+ initial returned_lock_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ returned_lock_valid <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY
+ && S_AXI_ARLOCK && S_AXI_ARID== lock_id)
+ returned_lock_valid <= 0;
+ else if (w_cancel_lock)
+ returned_lock_valid <= 0;
+ else if (rskd_valid && rskd_lock && rskd_ready)
+ returned_lock_valid <= lock_valid;
+ // }}}
+
+ // w_cancel_lock
+ // {{{
+ always @(*)
+ w_cancel_lock = (lock_valid && w_lock_request)
+ || (lock_valid && o_we
+ && o_waddr >= lock_start
+ && o_waddr <= lock_end
+ && o_wstrb != 0);
+ // }}}
+
+ // lock_valid
+ // {{{
+ initial lock_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_LOCK)
+ lock_valid <= 0;
+ else begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY
+ && S_AXI_ARLOCK && S_AXI_ARID== lock_id)
+ lock_valid <= 0;
+ if (w_cancel_lock)
+ lock_valid <= 0;
+ if (w_valid_lock_request)
+ lock_valid <= 1;
+ end
+ // }}}
+
+ // lock_start, lock_end, lock_len, lock_size, lock_id
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (w_valid_lock_request)
+ begin
+ lock_start <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB];
+ lock_end <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB]
+ + ((S_AXI_ARBURST == 2'b00) ? 0 : S_AXI_ARLEN[3:0]);
+ lock_len <= S_AXI_ARLEN[3:0];
+ lock_burst <= S_AXI_ARBURST;
+ lock_size <= S_AXI_ARSIZE;
+ lock_id <= S_AXI_ARID;
+ end
+ // }}}
+
+ // w_write_lock_valid
+ // {{{
+ always @(*)
+ begin
+ w_write_lock_valid = returned_lock_valid;
+ if (!m_awvalid || !m_awready || !m_awlock || !lock_valid)
+ w_write_lock_valid = 0;
+ if (m_awaddr[C_S_AXI_ADDR_WIDTH-1:LSB] != lock_start)
+ w_write_lock_valid = 0;
+ if (m_awid != lock_id)
+ w_write_lock_valid = 0;
+ if (m_awlen[3:0] != lock_len) // MAX transfer size is 16 beats
+ w_write_lock_valid = 0;
+ if (m_awburst != 2'b01 && lock_len != 0)
+ w_write_lock_valid = 0;
+ if (m_awsize != lock_size)
+ w_write_lock_valid = 0;
+ end
+ // }}}
+
+ assign write_lock_valid = w_write_lock_valid;
+ // }}}
+ end else if (OPT_LOCK) // && OPT_LOCKID
+ begin : EXCLUSIVE_ACCESS_PER_ID
+ // {{{
+
+ genvar gk;
+ wire [(1<<IW)-1:0] write_lock_valid_per_id;
+
+ for(gk=0; gk<(1<<IW); gk=gk+1)
+ begin : PER_ID_LOGIC
+ // {{{
+ // Local declarations
+ // {{{
+ reg w_valid_lock_request,
+ w_cancel_lock,
+ lock_valid, returned_lock_valid;
+ reg [1:0] lock_burst;
+ reg [2:0] lock_size;
+ reg [3:0] lock_len;
+ reg [AW-LSB-1:0] lock_start, lock_end;
+ reg w_write_lock_valid;
+ // }}}
+
+ // valid_lock_request
+ // {{{
+ always @(*)
+ begin
+ w_valid_lock_request = 0;
+ if (S_AXI_ARVALID && S_AXI_ARREADY
+ && S_AXI_ARID == gk[IW-1:0]
+ && S_AXI_ARLOCK)
+ w_valid_lock_request = 1;
+ if (o_we && o_waddr == S_AXI_ARADDR[AW-1:LSB])
+ w_valid_lock_request = 0;
+ end
+ // }}}
+
+ // returned_lock_valid
+ // {{{
+ initial returned_lock_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ returned_lock_valid <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY
+ &&S_AXI_ARLOCK&&S_AXI_ARID== gk[IW-1:0])
+ returned_lock_valid <= 0;
+ else if (w_cancel_lock)
+ returned_lock_valid <= 0;
+ else if (rskd_valid && rskd_lock && rskd_ready
+ && rskd_id == gk[IW-1:0])
+ returned_lock_valid <= lock_valid;
+ // }}}
+
+ // w_cancel_lock
+ // {{{
+ always @(*)
+ w_cancel_lock=(lock_valid&&w_valid_lock_request)
+ || (lock_valid && o_we
+ && o_waddr >= lock_start
+ && o_waddr <= lock_end
+ && o_wstrb != 0);
+ // }}}
+
+ // lock_valid
+ // {{{
+ initial lock_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_LOCK)
+ lock_valid <= 0;
+ else begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY
+ && S_AXI_ARLOCK
+ && S_AXI_ARID == gk[IW-1:0])
+ lock_valid <= 0;
+ if (w_cancel_lock)
+ lock_valid <= 0;
+ if (w_valid_lock_request)
+ lock_valid <= 1;
+ end
+ // }}}
+
+ // lock_start, lock_end, lock_len, lock_size
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (w_valid_lock_request)
+ begin
+ lock_start <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB];
+ // Verilator lint_off WIDTH
+ lock_end <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB]
+ + ((S_AXI_ARBURST == 2'b00) ? 4'h0 : S_AXI_ARLEN[3:0]);
+ // Verilator lint_on WIDTH
+ lock_len <= S_AXI_ARLEN[3:0];
+ lock_size <= S_AXI_ARSIZE;
+ lock_burst <= S_AXI_ARBURST;
+ end
+ // }}}
+
+ // w_write_lock_valid
+ // {{{
+ always @(*)
+ begin
+ w_write_lock_valid = returned_lock_valid;
+ if (!m_awvalid || !m_awready || !m_awlock || !lock_valid)
+ w_write_lock_valid = 0;
+ if (m_awaddr[C_S_AXI_ADDR_WIDTH-1:LSB] != lock_start)
+ w_write_lock_valid = 0;
+ if (m_awid[IW-1:0] != gk[IW-1:0])
+ w_write_lock_valid = 0;
+ if (m_awlen[3:0] != lock_len) // MAX transfer size is 16 beats
+ w_write_lock_valid = 0;
+ if (m_awburst != 2'b01 && lock_len != 0)
+ w_write_lock_valid = 0;
+ if (m_awsize != lock_size)
+ w_write_lock_valid = 0;
+ end
+ // }}}
+
+ assign write_lock_valid_per_id[gk]= w_write_lock_valid;
+ // }}}
+ end
+
+ assign write_lock_valid = |write_lock_valid_per_id;
+ // }}}
+ end else begin : NO_LOCKING
+ // {{{
+
+ assign write_lock_valid = 1'b0;
+ // Verilator lint_off UNUSED
+ wire unused_lock;
+ assign unused_lock = &{ 1'b0, S_AXI_ARLOCK, S_AXI_AWLOCK };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS,
+ S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The following properties are only some of the properties used
+ // to verify this core
+ //
+ // Local (formal) register declarations
+ // {{{
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(!S_AXI_ARESETN);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI slave properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH))
+ // ...
+ // }}}
+ f_slave(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awvalid(m_awvalid),
+ .i_axi_awready(m_awready),
+ .i_axi_awid( m_awid),
+ .i_axi_awaddr( m_awaddr),
+ .i_axi_awlen( m_awlen),
+ .i_axi_awsize( m_awsize),
+ .i_axi_awburst(m_awburst),
+ .i_axi_awlock( m_awlock),
+ .i_axi_awcache(4'h0),
+ .i_axi_awprot( 3'h0),
+ .i_axi_awqos( 4'h0),
+ // }}}
+ // Write Data Channel
+ // {{{
+ // Write Data
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ .i_axi_wlast(S_AXI_WLAST),
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ // }}}
+ // Write response
+ // {{{
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bid( S_AXI_BID),
+ .i_axi_bresp( S_AXI_BRESP),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_arid( S_AXI_ARID),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arlen( S_AXI_ARLEN),
+ .i_axi_arsize( S_AXI_ARSIZE),
+ .i_axi_arburst(S_AXI_ARBURST),
+ .i_axi_arlock( S_AXI_ARLOCK),
+ .i_axi_arcache(S_AXI_ARCACHE),
+ .i_axi_arprot( S_AXI_ARPROT),
+ .i_axi_arqos( S_AXI_ARQOS),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rid(S_AXI_RID),
+ .i_axi_rdata(S_AXI_RDATA),
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rlast(S_AXI_RLAST),
+ // }}}
+ //
+ // ...
+ // }}}
+ );
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_bvalid)
+ assert(S_AXI_BVALID);
+
+ always @(*)
+ assert(axi_awready == (!S_AXI_WREADY&& !r_bvalid));
+
+ always @(*)
+ if (axi_awready)
+ assert(!S_AXI_WREADY);
+
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+
+ //
+ // ...
+ //
+
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && axi_rlen == 0)
+ assert(S_AXI_ARREADY);
+
+ always @(posedge S_AXI_ACLK)
+ assert(axi_rlen <= 256);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Lowpower checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (OPT_LOWPOWER && S_AXI_ARESETN)
+ begin
+ if (!rskd_valid)
+ assert(!rskd_lock);
+ if (faxi_awr_nbursts == 0)
+ begin
+ assert(S_AXI_BRESP == 2'b00);
+ // assert(S_AXI_BID == 0);
+ end
+
+ if (!S_AXI_RVALID)
+ begin
+ assert(S_AXI_RID == 0);
+ assert(S_AXI_RDATA == 0);
+ assert(S_AXI_RRESP == 2'b00);
+ end
+
+ if (!o_we)
+ begin
+ assert(o_waddr == 0);
+ assert(o_wdata == 0);
+ assert(o_wstrb == 0);
+ end
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg f_wr_cvr_valid, f_rd_cvr_valid;
+ reg [4:0] f_dbl_rd_count, f_dbl_wr_count;
+
+ initial f_wr_cvr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_wr_cvr_valid <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN > 4)
+ f_wr_cvr_valid <= 1;
+
+ always @(*)
+ cover(!S_AXI_BVALID && axi_awready && !m_awvalid
+ && f_wr_cvr_valid /* && ... */));
+
+ initial f_rd_cvr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_rd_cvr_valid <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 4)
+ f_rd_cvr_valid <= 1;
+
+ always @(*)
+ cover(S_AXI_ARREADY && f_rd_cvr_valid /* && ... */);
+
+ //
+ // Generate cover statements associated with multiple successive bursts
+ //
+ // These will be useful for demonstrating the throughput of the core.
+ //
+
+ initial f_dbl_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dbl_wr_count = 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN == 3)
+ begin
+ if (!(&f_dbl_wr_count))
+ f_dbl_wr_count <= f_dbl_wr_count + 1;
+ end
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 1)); //!
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 3)); //!
+
+ always @(*)
+ cover(S_AXI_ARESETN && (f_dbl_wr_count > 3) && !m_awvalid
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID && !S_AXI_BVALID)
+ && (faxi_awr_nbursts == 0)
+ && (faxi_wr_pending == 0));
+
+ initial f_dbl_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_dbl_rd_count = 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 3)
+ begin
+ if (!(&f_dbl_rd_count))
+ f_dbl_rd_count <= f_dbl_rd_count + 1;
+ end
+
+ always @(*)
+ cover(!S_AXI_ARESETN && (f_dbl_rd_count > 3)
+ /* && ... */
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+
+ generate if (OPT_LOCK)
+ begin
+ always @(*)
+ cover(S_AXI_ARESETN && S_AXI_BVALID && S_AXI_BREADY
+ && S_AXI_BRESP == 2'b01);
+ end endgenerate
+
+`ifdef VERIFIC
+ cover property (@(posedge S_AXI_ACLK)
+ disable iff (!S_AXI_ARESETN)
+ // Accept a burst request for 4 beats
+ (S_AXI_ARVALID && S_AXI_ARREADY && (S_AXI_ARLEN == 3)
+ ##1 S_AXI_ARVALID [*3]) [*3]
+ ##1 1 [*0:12]
+ // The return to idle
+ ##1 (!S_AXI_ARVALID && !o_rd && !rskd_valid && !S_AXI_RVALID)
+ );
+`endif
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/easyaxil.v b/rtl/wb2axip/easyaxil.v
new file mode 100644
index 0000000..0c88b11
--- /dev/null
+++ b/rtl/wb2axip/easyaxil.v
@@ -0,0 +1,524 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: easyaxil
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Demonstrates a simple AXI-Lite interface.
+//
+// This was written in light of my last demonstrator, for which others
+// declared that it was much too complicated to understand. The goal of
+// this demonstrator is to have logic that's easier to understand, use,
+// and copy as needed.
+//
+// Since there are two basic approaches to AXI-lite signaling, both with
+// and without skidbuffers, this example demonstrates both so that the
+// differences can be compared and contrasted.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module easyaxil #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ parameter C_AXI_ADDR_WIDTH = 4,
+ localparam C_AXI_DATA_WIDTH = 32,
+ parameter [0:0] OPT_SKIDBUFFER = 1'b0,
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr;
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ reg [31:0] r0, r1, r2, r3;
+ wire [31:0] wskd_r0, wskd_r1, wskd_r2, wskd_r3;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_WRITE
+ // {{{
+ wire awskd_valid, wskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilawskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr));
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8))
+ axilwskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb }));
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+ // }}}
+ end else begin : SIMPLE_WRITES
+ // {{{
+ reg axil_awready;
+
+ initial axil_awready = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axil_awready <= 1'b0;
+ else
+ axil_awready <= !axil_awready
+ && (S_AXI_AWVALID && S_AXI_WVALID)
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ assign S_AXI_AWREADY = axil_awready;
+ assign S_AXI_WREADY = axil_awready;
+
+ assign awskd_addr = S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign wskd_data = S_AXI_WDATA;
+ assign wskd_strb = S_AXI_WSTRB;
+
+ assign axil_write_ready = axil_awready;
+ // }}}
+ end endgenerate
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+ assign S_AXI_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_READ
+ // {{{
+ wire arskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXI_RREADY);
+ // }}}
+ end else begin : SIMPLE_READS
+ // {{{
+ reg axil_arready;
+
+ always @(*)
+ axil_arready = !S_AXI_RVALID;
+
+ assign arskd_addr = S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign S_AXI_ARREADY = axil_arready;
+ assign axil_read_ready = (S_AXI_ARVALID && S_AXI_ARREADY);
+ // }}}
+ end endgenerate
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+ assign S_AXI_RDATA = axil_read_data;
+ assign S_AXI_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // apply_wstrb(old_data, new_data, write_strobes)
+ assign wskd_r0 = apply_wstrb(r0, wskd_data, wskd_strb);
+ assign wskd_r1 = apply_wstrb(r1, wskd_data, wskd_strb);
+ assign wskd_r2 = apply_wstrb(r2, wskd_data, wskd_strb);
+ assign wskd_r3 = apply_wstrb(r3, wskd_data, wskd_strb);
+
+ initial r0 = 0;
+ initial r1 = 0;
+ initial r2 = 0;
+ initial r3 = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ begin
+ r0 <= 0;
+ r1 <= 0;
+ r2 <= 0;
+ r3 <= 0;
+ end else if (axil_write_ready)
+ begin
+ case(awskd_addr)
+ 2'b00: r0 <= wskd_r0;
+ 2'b01: r1 <= wskd_r1;
+ 2'b10: r2 <= wskd_r2;
+ 2'b11: r3 <= wskd_r3;
+ endcase
+ end
+
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ case(arskd_addr)
+ 2'b00: axil_read_data <= r0;
+ 2'b01: axil_read_data <= r1;
+ 2'b10: axil_read_data <= r2;
+ 2'b11: axil_read_data <= r3;
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+
+ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb;
+ input [C_AXI_DATA_WIDTH-1:0] prior_data;
+ input [C_AXI_DATA_WIDTH-1:0] new_data;
+ input [C_AXI_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_ARADDR[ADDRLSB-1:0],
+ S_AXI_AWADDR[ADDRLSB-1:0] };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(3),
+ .F_AXI_MAXDELAY(3),
+ .F_AXI_MAXRSTALL(5),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .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)
+ // }}}
+ );
+
+ always @(*)
+ if (OPT_SKIDBUFFER)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end else begin
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0));
+ assert(faxil_awr_outstanding == faxil_wr_outstanding);
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXI_RVALID)
+ assert(S_AXI_RDATA == 0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register return checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`define CHECK_REGISTERS
+`ifdef CHECK_REGISTERS
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR(0)
+ // }}}
+ ) fr0 (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(r0)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR(4)
+ // }}}
+ ) fr1 (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(r1)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR(8)
+ // }}}
+ ) fr2 (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(r2)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR(12)
+ // }}}
+ ) fr3 (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(r3)
+ // }}}
+ );
+`endif
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // {{{
+
+ // While there are already cover properties in the formal property
+ // set above, you'll probably still want to cover something
+ // application specific here
+
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/migsdram.v b/rtl/wb2axip/migsdram.v
new file mode 100644
index 0000000..c1671c8
--- /dev/null
+++ b/rtl/wb2axip/migsdram.v
@@ -0,0 +1,313 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: migsdram.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: This file isn't really a part of the synthesis implementation
+// of the wb2axip project itself, but rather it is an example
+// of how the wb2axip project can be used to connect a MIG generated
+// IP component.
+//
+// This implementation depends upon the existence of a MIG generated
+// core, named "mig_axis", and illustrates how such a core might be
+// connected to the wbm2axip bridge. Specific options of the mig_axis
+// setup include 6 identifier bits, and a full-sized bus width of 128
+// bits. These two settings are both appropriate for driving a DDR3
+// memory (whose minimum transfer size is 128 bits), but may need to be
+// adjusted to support other memories.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2015-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module migsdram(i_clk, i_clk_200mhz, o_sys_clk, i_rst, o_sys_reset,
+ // Wishbone components
+ i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
+ // SDRAM connections
+ o_ddr_ck_p, o_ddr_ck_n,
+ o_ddr_reset_n, o_ddr_cke,
+ o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n,
+ o_ddr_ba, o_ddr_addr,
+ o_ddr_odt, o_ddr_dm,
+ io_ddr_dqs_p, io_ddr_dqs_n,
+ io_ddr_data
+ );
+ parameter DDRWIDTH = 16, WBDATAWIDTH=32;
+ parameter AXIDWIDTH = 6;
+ // The SDRAM address bits (RAMABITS) are a touch more difficult to work
+ // out. Here we leave them as a fixed parameter, but there are
+ // consequences to this. Specifically, the wishbone data width, the
+ // wishbone address width, and this number have interactions not
+ // well captured here.
+ parameter RAMABITS = 28;
+ // All DDR3 memories have 8 timeslots. This, if the DDR3 memory
+ // has 16 bits to it (as above), the entire transaction must take
+ // AXIWIDTH bits ...
+ localparam AXIWIDTH= DDRWIDTH *8;
+ localparam DW=WBDATAWIDTH;
+ localparam AW=(WBDATAWIDTH==32)? RAMABITS-2
+ :((WBDATAWIDTH==64) ? RAMABITS-3
+ :((WBDATAWIDTH==128) ? RAMABITS-4
+ : RAMABITS-5)); // (WBDATAWIDTH==256)
+ localparam SELW= (WBDATAWIDTH/8);
+ //
+ input wire i_clk, i_clk_200mhz, i_rst;
+ output wire o_sys_clk;
+ output reg o_sys_reset;
+ //
+ input wire i_wb_cyc, i_wb_stb, i_wb_we;
+ input wire [(AW-1):0] i_wb_addr;
+ input wire [(DW-1):0] i_wb_data;
+ input wire [(SELW-1):0] i_wb_sel;
+ output wire o_wb_ack, o_wb_stall;
+ output wire [(DW-1):0] o_wb_data;
+ output wire o_wb_err;
+ //
+ output wire [0:0] o_ddr_ck_p, o_ddr_ck_n;
+ output wire [0:0] o_ddr_cke;
+ output wire o_ddr_reset_n,
+ o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n;
+ output wire [0:0] o_ddr_cs_n;
+ output wire [2:0] o_ddr_ba;
+ output wire [13:0] o_ddr_addr;
+ output wire [0:0] o_ddr_odt;
+ output wire [(DDRWIDTH/8-1):0] o_ddr_dm;
+ inout wire [1:0] io_ddr_dqs_p, io_ddr_dqs_n;
+ inout wire [(DDRWIDTH-1):0] io_ddr_data;
+
+
+`define SDRAM_ACCESS
+`ifdef SDRAM_ACCESS
+
+ wire aresetn;
+ assign aresetn = 1'b1; // Never reset
+
+ // Write address channel
+ wire [(AXIDWIDTH-1):0] s_axi_awid;
+ wire [(RAMABITS-1):0] s_axi_awaddr;
+ wire [7:0] s_axi_awlen;
+ wire [2:0] s_axi_awsize;
+ wire [1:0] s_axi_awburst;
+ wire [0:0] s_axi_awlock;
+ wire [3:0] s_axi_awcache;
+ wire [2:0] s_axi_awprot;
+ wire [3:0] s_axi_awqos;
+ wire s_axi_awvalid;
+ wire s_axi_awready;
+ // Writei data channel
+ wire [(AXIWIDTH-1):0] s_axi_wdata;
+ wire [(AXIWIDTH/8-1):0] s_axi_wstrb;
+ wire s_axi_wlast, s_axi_wvalid, s_axi_wready;
+ // Write response channel
+ wire s_axi_bready;
+ wire [(AXIDWIDTH-1):0] s_axi_bid;
+ wire [1:0] s_axi_bresp;
+ wire s_axi_bvalid;
+
+ // Read address channel
+ wire [(AXIDWIDTH-1):0] s_axi_arid;
+ wire [(RAMABITS-1):0] s_axi_araddr;
+ wire [7:0] s_axi_arlen;
+ wire [2:0] s_axi_arsize;
+ wire [1:0] s_axi_arburst;
+ wire [0:0] s_axi_arlock;
+ wire [3:0] s_axi_arcache;
+ wire [2:0] s_axi_arprot;
+ wire [3:0] s_axi_arqos;
+ wire s_axi_arvalid;
+ wire s_axi_arready;
+ // Read response/data channel
+ wire [(AXIDWIDTH-1):0] s_axi_rid;
+ wire [(AXIWIDTH-1):0] s_axi_rdata;
+ wire [1:0] s_axi_rresp;
+ wire s_axi_rlast;
+ wire s_axi_rvalid;
+ wire s_axi_rready;
+
+ // Other wires ...
+ wire init_calib_complete, mmcm_locked;
+ wire app_sr_active, app_ref_ack, app_zq_ack;
+ wire app_sr_req, app_ref_req, app_zq_req;
+ wire w_sys_reset;
+ wire [11:0] w_device_temp;
+
+
+ mig_axis mig_sdram(
+ .ddr3_ck_p(o_ddr_ck_p), .ddr3_ck_n(o_ddr_ck_n),
+ .ddr3_reset_n(o_ddr_reset_n), .ddr3_cke(o_ddr_cke),
+ .ddr3_cs_n(o_ddr_cs_n), .ddr3_ras_n(o_ddr_ras_n),
+ .ddr3_we_n(o_ddr_we_n), .ddr3_cas_n(o_ddr_cas_n),
+ .ddr3_ba(o_ddr_ba), .ddr3_addr(o_ddr_addr),
+ .ddr3_odt(o_ddr_odt),
+ .ddr3_dqs_p(io_ddr_dqs_p), .ddr3_dqs_n(io_ddr_dqs_n),
+ .ddr3_dq(io_ddr_data), .ddr3_dm(o_ddr_dm),
+ //
+ .sys_clk_i(i_clk),
+ .clk_ref_i(i_clk_200mhz),
+ .ui_clk(o_sys_clk),
+ .ui_clk_sync_rst(w_sys_reset),
+ .mmcm_locked(mmcm_locked),
+ .aresetn(aresetn),
+ .app_sr_req(1'b0),
+ .app_ref_req(1'b0),
+ .app_zq_req(1'b0),
+ .app_sr_active(app_sr_active),
+ .app_ref_ack(app_ref_ack),
+ .app_zq_ack(app_zq_ack),
+ //
+ .s_axi_awid(s_axi_awid), .s_axi_awaddr(s_axi_awaddr),
+ .s_axi_awlen(s_axi_awlen), .s_axi_awsize(s_axi_awsize),
+ .s_axi_awburst(s_axi_awburst), .s_axi_awlock(s_axi_awlock),
+ .s_axi_awcache(s_axi_awcache), .s_axi_awprot(s_axi_awprot),
+ .s_axi_awqos(s_axi_awqos), .s_axi_awvalid(s_axi_awvalid),
+ .s_axi_awready(s_axi_awready),
+ //
+ .s_axi_wready( s_axi_wready),
+ .s_axi_wdata( s_axi_wdata),
+ .s_axi_wstrb( s_axi_wstrb),
+ .s_axi_wlast( s_axi_wlast),
+ .s_axi_wvalid( s_axi_wvalid),
+ //
+ .s_axi_bready(s_axi_bready), .s_axi_bid(s_axi_bid),
+ .s_axi_bresp(s_axi_bresp), .s_axi_bvalid(s_axi_bvalid),
+ //
+ .s_axi_arid(s_axi_arid), .s_axi_araddr(s_axi_araddr),
+ .s_axi_arlen(s_axi_arlen), .s_axi_arsize(s_axi_arsize),
+ .s_axi_arburst(s_axi_arburst), .s_axi_arlock(s_axi_arlock),
+ .s_axi_arcache(s_axi_arcache), .s_axi_arprot(s_axi_arprot),
+ .s_axi_arqos(s_axi_arqos), .s_axi_arvalid(s_axi_arvalid),
+ .s_axi_arready(s_axi_arready),
+ //
+ .s_axi_rready(s_axi_rready), .s_axi_rid(s_axi_rid),
+ .s_axi_rdata(s_axi_rdata), .s_axi_rresp(s_axi_rresp),
+ .s_axi_rlast(s_axi_rlast), .s_axi_rvalid(s_axi_rvalid),
+ .init_calib_complete(init_calib_complete),
+ .sys_rst(i_rst),
+ .device_temp(w_device_temp)
+ );
+
+ wbm2axisp #(
+ .C_AXI_ID_WIDTH(AXIDWIDTH),
+ .C_AXI_DATA_WIDTH(AXIWIDTH),
+ .C_AXI_ADDR_WIDTH(RAMABITS),
+ .AW(AW), .DW(DW)
+ )
+ bus_translator(
+ .i_clk(o_sys_clk),
+ // .i_reset(i_rst), // internally unused
+ // Write address channel signals
+ .o_axi_awvalid( s_axi_awvalid),
+ .i_axi_awready( s_axi_awready),
+ .o_axi_awid( s_axi_awid),
+ .o_axi_awaddr( s_axi_awaddr),
+ .o_axi_awlen( s_axi_awlen),
+ .o_axi_awsize( s_axi_awsize),
+ .o_axi_awburst( s_axi_awburst),
+ .o_axi_awlock( s_axi_awlock),
+ .o_axi_awcache( s_axi_awcache),
+ .o_axi_awprot( s_axi_awprot), // s_axi_awqos
+ .o_axi_awqos( s_axi_awqos), // s_axi_awqos
+ //
+ .o_axi_wvalid( s_axi_wvalid),
+ .i_axi_wready( s_axi_wready),
+ .o_axi_wdata( s_axi_wdata),
+ .o_axi_wstrb( s_axi_wstrb),
+ .o_axi_wlast( s_axi_wlast),
+ //
+ .i_axi_bvalid( s_axi_bvalid),
+ .o_axi_bready( s_axi_bready),
+ .i_axi_bid( s_axi_bid),
+ .i_axi_bresp( s_axi_bresp),
+ //
+ .o_axi_arvalid( s_axi_arvalid),
+ .i_axi_arready( s_axi_arready),
+ .o_axi_arid( s_axi_arid),
+ .o_axi_araddr( s_axi_araddr),
+ .o_axi_arlen( s_axi_arlen),
+ .o_axi_arsize( s_axi_arsize),
+ .o_axi_arburst( s_axi_arburst),
+ .o_axi_arlock( s_axi_arlock),
+ .o_axi_arcache( s_axi_arcache),
+ .o_axi_arprot( s_axi_arprot),
+ .o_axi_arqos( s_axi_arqos),
+ //
+ .i_axi_rvalid( s_axi_rvalid),
+ .o_axi_rready( s_axi_rready),
+ .i_axi_rid( s_axi_rid),
+ .i_axi_rdata( s_axi_rdata),
+ .i_axi_rresp( s_axi_rresp),
+ .i_axi_rlast( s_axi_rlast),
+ //
+ .i_wb_cyc( i_wb_cyc),
+ .i_wb_stb( i_wb_stb),
+ .i_wb_we( i_wb_we),
+ .i_wb_addr( i_wb_addr),
+ .i_wb_data( i_wb_data),
+ .i_wb_sel( i_wb_sel),
+ //
+ .o_wb_stall( o_wb_stall),
+ .o_wb_ack( o_wb_ack),
+ .o_wb_data( o_wb_data),
+ .o_wb_err( o_wb_err)
+ );
+
+ // Convert from active low to active high, *and* hold the system in
+ // reset until the memory comes up.
+ initial o_sys_reset = 1'b1;
+ always @(posedge o_sys_clk)
+ o_sys_reset <= (!w_sys_reset)
+ ||(!init_calib_complete)
+ ||(!mmcm_locked);
+`else
+ BUFG sysclk(i_clk, o_sys_clk);
+ initial o_sys_reset <= 1'b1;
+ always @(posedge i_clk)
+ o_sys_reset <= 1'b1;
+
+ OBUFDS ckobuf(.I(i_clk), .O(o_ddr_ck_p), .OB(o_ddr_ck_n));
+
+ assign o_ddr_reset_n = 1'b0;
+ assign o_ddr_cke[0] = 1'b0;
+ assign o_ddr_cs_n[0] = 1'b1;
+ assign o_ddr_cas_n = 1'b1;
+ assign o_ddr_ras_n = 1'b1;
+ assign o_ddr_we_n = 1'b1;
+ assign o_ddr_ba = 3'h0;
+ assign o_ddr_addr = 14'h00;
+ assign o_ddr_dm = 2'b00;
+ assign io_ddr_data = 16'h0;
+
+ OBUFDS dqsbufa(.I(i_clk), .O(io_ddr_dqs_p[1]), .OB(io_ddr_dqs_n[1]));
+ OBUFDS dqsbufb(.I(i_clk), .O(io_ddr_dqs_p[0]), .OB(io_ddr_dqs_n[0]));
+
+`endif
+
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/mod.mk b/rtl/wb2axip/mod.mk
new file mode 100644
index 0000000..224f290
--- /dev/null
+++ b/rtl/wb2axip/mod.mk
@@ -0,0 +1,10 @@
+cores := axixbar
+
+define core
+ $(this)/rtl_dirs := .
+endef
+
+define core/axixbar
+ $(this)/deps := wb2axip
+ $(this)/rtl_top := axixbar
+endef
diff --git a/rtl/wb2axip/sfifo.v b/rtl/wb2axip/sfifo.v
new file mode 100644
index 0000000..c60dffe
--- /dev/null
+++ b/rtl/wb2axip/sfifo.v
@@ -0,0 +1,482 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: sfifo.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A synchronous data FIFO.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// Written and distributed by Gisselquist Technology, LLC
+// }}}
+// This design is hereby granted to the public domain.
+// {{{
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module sfifo #(
+ // {{{
+ parameter BW=8, // Byte/data width
+ parameter LGFLEN=4,
+ parameter [0:0] OPT_ASYNC_READ = 1'b1,
+ parameter [0:0] OPT_WRITE_ON_FULL = 1'b0,
+ parameter [0:0] OPT_READ_ON_EMPTY = 1'b0
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk,
+ input wire i_reset,
+ //
+ // Write interface
+ input wire i_wr,
+ input wire [(BW-1):0] i_data,
+ output wire o_full,
+ output reg [LGFLEN:0] o_fill,
+ //
+ // Read interface
+ input wire i_rd,
+ output reg [(BW-1):0] o_data,
+ output wire o_empty // True if FIFO is empty
+`ifdef FORMAL
+`ifdef F_PEEK
+ , output wire [LGFLEN:0] f_first_addr,
+ output wire [LGFLEN:0] f_second_addr,
+ output reg [BW-1:0] f_first_data, f_second_data,
+
+ output reg f_first_in_fifo,
+ f_second_in_fifo,
+ output reg [LGFLEN:0] f_distance_to_first,
+ f_distance_to_second
+`endif
+`endif
+ // }}}
+ );
+
+ // Register/net declarations
+ // {{{
+ localparam FLEN=(1<<LGFLEN);
+ reg r_full, r_empty;
+ reg [(BW-1):0] mem[0:(FLEN-1)];
+ reg [LGFLEN:0] wr_addr, rd_addr;
+
+ wire w_wr = (i_wr && !o_full);
+ wire w_rd = (i_rd && !o_empty);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write half
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_fill
+ // {{{
+ initial o_fill = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_fill <= 0;
+ else case({ w_wr, w_rd })
+ 2'b01: o_fill <= o_fill - 1;
+ 2'b10: o_fill <= o_fill + 1;
+ default: o_fill <= wr_addr - rd_addr;
+ endcase
+ // }}}
+
+ // r_full, o_full
+ // {{{
+ initial r_full = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_full <= 0;
+ else case({ w_wr, w_rd})
+ 2'b01: r_full <= 1'b0;
+ 2'b10: r_full <= (o_fill == { 1'b0, {(LGFLEN){1'b1}} });
+ default: r_full <= (o_fill == { 1'b1, {(LGFLEN){1'b0}} });
+ endcase
+
+ assign o_full = (i_rd && OPT_WRITE_ON_FULL) ? 1'b0 : r_full;
+ // }}}
+
+ // wr_addr, the write address pointer
+ // {{{
+ initial wr_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ wr_addr <= 0;
+ else if (w_wr)
+ wr_addr <= wr_addr + 1'b1;
+ // }}}
+
+ // Write to memory
+ // {{{
+ always @(posedge i_clk)
+ if (w_wr)
+ mem[wr_addr[(LGFLEN-1):0]] <= i_data;
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read half
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // rd_addr, the read address pointer
+ // {{{
+ initial rd_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ rd_addr <= 0;
+ else if (w_rd)
+ rd_addr <= rd_addr + 1;
+ // }}}
+
+ // r_empty, o_empty
+ // {{{
+ initial r_empty = 1'b1;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_empty <= 1'b1;
+ else case ({ w_wr, w_rd })
+ 2'b01: r_empty <= (o_fill <= 1);
+ 2'b10: r_empty <= 1'b0;
+ default: begin end
+ endcase
+
+ assign o_empty = (OPT_READ_ON_EMPTY && i_wr) ? 1'b0 : r_empty;
+ // }}}
+
+ // Read from the FIFO
+ // {{{
+ generate if (OPT_ASYNC_READ && OPT_READ_ON_EMPTY)
+ begin : ASYNCHRONOUS_READ_ON_EMPTY
+ // o_data
+ // {{{
+ always @(*)
+ begin
+ o_data = mem[rd_addr[LGFLEN-1:0]];
+ if (r_empty)
+ o_data = i_data;
+ end
+ // }}}
+ end else if (OPT_ASYNC_READ)
+ begin : ASYNCHRONOUS_READ
+ // o_data
+ // {{{
+ always @(*)
+ o_data = mem[rd_addr[LGFLEN-1:0]];
+ // }}}
+ end else begin : REGISTERED_READ
+ // {{{
+ reg bypass_valid;
+ reg [BW-1:0] bypass_data, rd_data;
+ reg [LGFLEN-1:0] rd_next;
+
+ always @(*)
+ rd_next = rd_addr[LGFLEN-1:0] + 1;
+
+ // Memory read, bypassing it if we must
+ // {{{
+ initial bypass_valid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ bypass_valid <= 0;
+ else if (r_empty || i_rd)
+ begin
+ if (!i_wr)
+ bypass_valid <= 1'b0;
+ else if (r_empty || (i_rd && (o_fill == 1)))
+ bypass_valid <= 1'b1;
+ else
+ bypass_valid <= 1'b0;
+ end
+
+ always @(posedge i_clk)
+ if (r_empty || i_rd)
+ bypass_data <= i_data;
+
+ initial mem[0] = 0;
+ initial rd_data = 0;
+ always @(posedge i_clk)
+ if (w_rd)
+ rd_data <= mem[rd_next];
+
+ always @(*)
+ if (OPT_READ_ON_EMPTY && r_empty)
+ o_data = i_data;
+ else if (bypass_valid)
+ o_data = bypass_data;
+ else
+ o_data = rd_data;
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// FORMAL METHODS
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+`ifdef FORMAL
+
+//
+// Assumptions about our input(s)
+//
+//
+`ifdef SFIFO
+`define ASSUME assume
+`else
+`define ASSUME assert
+`endif
+
+ reg f_past_valid;
+ wire [LGFLEN:0] f_fill, f_next;
+ wire f_empty;
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assertions about our flags and counters
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ assign f_fill = wr_addr - rd_addr;
+ assign f_empty = (wr_addr == rd_addr);
+ assign f_next = rd_addr + 1'b1;
+
+ always @(*)
+ begin
+ assert(f_fill <= { 1'b1, {(LGFLEN){1'b0}} });
+ assert(o_fill == f_fill);
+
+ assert(r_full == (f_fill == {1'b1, {(LGFLEN){1'b0}} }));
+ assert(r_empty == (f_fill == 0));
+
+ if (!OPT_WRITE_ON_FULL)
+ begin
+ assert(o_full == r_full);
+ end else begin
+ assert(o_full == (r_full && !i_rd));
+ end
+
+ if (!OPT_READ_ON_EMPTY)
+ begin
+ assert(o_empty == r_empty);
+ end else begin
+ assert(o_empty == (r_empty && !i_wr));
+ end
+ end
+
+ always @(posedge i_clk)
+ if (!OPT_ASYNC_READ && f_past_valid)
+ begin
+ if (f_fill == 0)
+ begin
+ assert(r_empty);
+ assert(o_empty || (OPT_READ_ON_EMPTY && i_wr));
+ end else if ($past(f_fill)>1)
+ begin
+ assert(!r_empty);
+ end else if ($past(!i_rd && f_fill > 0))
+ assert(!r_empty);
+ end
+
+ always @(*)
+ if (!r_empty)
+ begin
+ // This also applies for the registered read case
+ assert(mem[rd_addr[LGFLEN-1:0]] == o_data);
+ end else if (OPT_READ_ON_EMPTY)
+ assert(o_data == i_data);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract: (Twin write test)
+ // {{{
+ // If you write two values in succession, you should be able to read
+ // those same two values in succession some time later.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Verilator lint_off UNDRIVEN
+ (* anyconst *) reg [LGFLEN:0] fw_first_addr;
+ // Verilator lint_on UNDRIVEN
+`ifndef F_PEEK
+ wire [LGFLEN:0] f_first_addr;
+ wire [LGFLEN:0] f_second_addr;
+ reg [BW-1:0] f_first_data, f_second_data;
+
+ reg f_first_in_fifo, f_second_in_fifo;
+ reg [LGFLEN:0] f_distance_to_first, f_distance_to_second;
+`endif
+ reg f_first_addr_in_fifo, f_second_addr_in_fifo;
+
+ assign f_first_addr = fw_first_addr;
+ assign f_second_addr = f_first_addr + 1;
+
+ always @(*)
+ begin
+ f_distance_to_first = (f_first_addr - rd_addr);
+ f_first_addr_in_fifo = 0;
+ if ((f_fill != 0) && (f_distance_to_first < f_fill))
+ f_first_addr_in_fifo = 1;
+ end
+
+ always @(*)
+ begin
+ f_distance_to_second = (f_second_addr - rd_addr);
+ f_second_addr_in_fifo = 0;
+ if ((f_fill != 0) && (f_distance_to_second < f_fill))
+ f_second_addr_in_fifo = 1;
+ end
+
+ always @(posedge i_clk)
+ if (w_wr && wr_addr == f_first_addr)
+ f_first_data <= i_data;
+
+ always @(posedge i_clk)
+ if (w_wr && wr_addr == f_second_addr)
+ f_second_data <= i_data;
+
+ always @(*)
+ if (f_first_addr_in_fifo)
+ assert(mem[f_first_addr[LGFLEN-1:0]] == f_first_data);
+ always @(*)
+ f_first_in_fifo = (f_first_addr_in_fifo && (mem[f_first_addr[LGFLEN-1:0]] == f_first_data));
+
+ always @(*)
+ if (f_second_addr_in_fifo)
+ assert(mem[f_second_addr[LGFLEN-1:0]] == f_second_data);
+
+ always @(*)
+ f_second_in_fifo = (f_second_addr_in_fifo && (mem[f_second_addr[LGFLEN-1:0]] == f_second_data));
+
+ always @(*)
+ if (f_first_in_fifo && (o_fill == 1 || f_distance_to_first == 0))
+ assert(o_data == f_first_data);
+
+ always @(*)
+ if (f_second_in_fifo && (o_fill == 1 || f_distance_to_second == 0))
+ assert(o_data == f_second_data);
+
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset))
+ begin
+ case({$past(f_first_in_fifo), $past(f_second_in_fifo)})
+ 2'b00: begin
+ if ($past(w_wr && (!w_rd || !r_empty))
+ &&($past(wr_addr == f_first_addr)))
+ begin
+ assert(f_first_in_fifo);
+ end else begin
+ assert(!f_first_in_fifo);
+ end
+ //
+ // The second could be in the FIFO, since
+ // one might write other data than f_first_data
+ //
+ // assert(!f_second_in_fifo);
+ end
+ 2'b01: begin
+ assert(!f_first_in_fifo);
+ if ($past(w_rd && (rd_addr==f_second_addr)))
+ begin
+ assert((o_empty&&!OPT_ASYNC_READ)||!f_second_in_fifo);
+ end else begin
+ assert(f_second_in_fifo);
+ end
+ end
+ 2'b10: begin
+ if ($past(w_wr)
+ &&($past(wr_addr == f_second_addr)))
+ begin
+ assert(f_second_in_fifo);
+ end else begin
+ assert(!f_second_in_fifo);
+ end
+ if ($past(!w_rd ||(rd_addr != f_first_addr)))
+ assert(f_first_in_fifo);
+ end
+ 2'b11: begin
+ assert(f_second_in_fifo);
+ if ($past(!w_rd ||(rd_addr != f_first_addr)))
+ begin
+ assert(f_first_in_fifo);
+ if (rd_addr == f_first_addr)
+ assert(o_data == f_first_data);
+ end else begin
+ assert(!f_first_in_fifo);
+ assert(o_data == f_second_data);
+ end
+ end
+ endcase
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`ifdef SFIFO
+ reg f_was_full;
+ initial f_was_full = 0;
+ always @(posedge i_clk)
+ if (o_full)
+ f_was_full <= 1;
+
+ always @(posedge i_clk)
+ cover($fell(f_empty));
+
+ always @(posedge i_clk)
+ cover($fell(o_empty));
+
+ always @(posedge i_clk)
+ cover(f_was_full && f_empty);
+
+ always @(posedge i_clk)
+ cover($past(o_full,2)&&(!$past(o_full))&&(o_full));
+
+ always @(posedge i_clk)
+ if (f_past_valid)
+ cover($past(o_empty,2)&&(!$past(o_empty))&& o_empty);
+`endif
+ // }}}
+
+ // Make Verilator happy
+ // Verilator lint_off UNUSED
+ wire unused_formal;
+ assign unused_formal = &{ 1'b0, f_next[LGFLEN], f_empty };
+ // Verilator lint_on UNUSED
+`endif // FORMAL
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/sfifothresh.v b/rtl/wb2axip/sfifothresh.v
new file mode 100644
index 0000000..5d4a156
--- /dev/null
+++ b/rtl/wb2axip/sfifothresh.v
@@ -0,0 +1,100 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: sfifothresh.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A synchronous data FIFO, generated from sfifo.v. This
+// particular version extends the FIFO interface with a threshold
+// calculator, to create an interrupt/signal when the FIFO has greater
+// than the threshold elements within it.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module sfifothresh(i_clk, i_reset,
+ i_wr, i_data, o_full, o_fill,
+ i_rd, o_data, o_empty,
+ i_threshold, o_int);
+ parameter BW=8; // Byte/data width
+ parameter LGFLEN=4;
+ parameter [0:0] OPT_ASYNC_READ = 1'b1;
+ localparam FLEN=(1<<LGFLEN);
+
+ //
+ //
+ input wire i_clk;
+ input wire i_reset;
+ //
+ // Write interface
+ input wire i_wr;
+ input wire [(BW-1):0] i_data;
+ output wire o_full;
+ output wire [LGFLEN:0] o_fill;
+ //
+ // Read interface
+ input wire i_rd;
+ output wire [(BW-1):0] o_data;
+ output wire o_empty; // True if FIFO is empty
+ //
+ input wire [LGFLEN:0] i_threshold;
+ output reg o_int;
+
+ wire w_wr = (i_wr && !o_full);
+ wire w_rd = (i_rd && !o_empty);
+
+ sfifo #(
+ .BW(BW), .LGFLEN(LGFLEN), .OPT_ASYNC_READ(OPT_ASYNC_READ)
+ ) sfifoi(
+ i_clk, i_reset, i_wr, i_data, o_full, o_fill, i_rd,
+ o_data, o_empty
+ );
+
+ initial o_int = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_int <= 0;
+ else case({ w_wr, w_rd })
+ 2'b01: o_int <= (o_fill-1 >= i_threshold);
+ 2'b10: o_int <= (o_fill+1 >= i_threshold);
+ default: o_int <= o_fill >= i_threshold;
+ endcase
+
+`ifdef FORMAL
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ always @(posedge i_clk)
+ if (!f_past_valid || $past(i_reset))
+ assert(!o_int);
+ else
+ assert(o_int == (o_fill >= $past(i_threshold)));
+`endif
+endmodule
diff --git a/rtl/wb2axip/skidbuffer.v b/rtl/wb2axip/skidbuffer.v
new file mode 100644
index 0000000..3d19cbb
--- /dev/null
+++ b/rtl/wb2axip/skidbuffer.v
@@ -0,0 +1,495 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: skidbuffer.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A basic SKID buffer.
+// {{{
+// Skid buffers are required for high throughput AXI code, since the AXI
+// specification requires that all outputs be registered. This means
+// that, if there are any stall conditions calculated, it will take a clock
+// cycle before the stall can be propagated up stream. This means that
+// the data will need to be buffered for a cycle until the stall signal
+// can make it to the output.
+//
+// Handling that buffer is the purpose of this core.
+//
+// On one end of this core, you have the i_valid and i_data inputs to
+// connect to your bus interface. There's also a registered o_ready
+// signal to signal stalls for the bus interface.
+//
+// The other end of the core has the same basic interface, but it isn't
+// registered. This allows you to interact with the bus interfaces
+// as though they were combinatorial logic, by interacting with this half
+// of the core.
+//
+// If at any time the incoming !stall signal, i_ready, signals a stall,
+// the incoming data is placed into a buffer. Internally, that buffer
+// is held in r_data with the r_valid flag used to indicate that valid
+// data is within it.
+// }}}
+// Parameters:
+// {{{
+// DW or data width
+// In order to make this core generic, the width of the data in the
+// skid buffer is parameterized
+//
+// OPT_LOWPOWER
+// Forces both o_data and r_data to zero if the respective *VALID
+// signal is also low. While this costs extra logic, it can also
+// be used to guarantee that any unused values aren't toggling and
+// therefore unnecessarily using power.
+//
+// This excess toggling can be particularly problematic if the
+// bus signals have a high fanout rate, or a long signal path
+// across an FPGA.
+//
+// OPT_OUTREG
+// Causes the outputs to be registered
+//
+// OPT_PASSTHROUGH
+// Turns the skid buffer into a passthrough. Used for formal
+// verification only.
+// }}}
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module skidbuffer #(
+ // {{{
+ parameter [0:0] OPT_LOWPOWER = 0,
+ parameter [0:0] OPT_OUTREG = 1,
+ //
+ parameter [0:0] OPT_PASSTHROUGH = 0,
+ parameter DW = 8,
+ parameter [0:0] OPT_INITIAL = 1'b1
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ input wire i_valid,
+ output wire o_ready,
+ input wire [DW-1:0] i_data,
+ output wire o_valid,
+ input wire i_ready,
+ output reg [DW-1:0] o_data
+ // }}}
+ );
+
+ wire [DW-1:0] w_data;
+
+ generate if (OPT_PASSTHROUGH)
+ begin : PASSTHROUGH
+ // {{{
+ assign { o_valid, o_ready } = { i_valid, i_ready };
+
+ always @(*)
+ if (!i_valid && OPT_LOWPOWER)
+ o_data = 0;
+ else
+ o_data = i_data;
+
+ assign w_data = 0;
+
+ // Keep Verilator happy
+ // Verilator lint_off UNUSED
+ // {{{
+ wire unused_passthrough;
+ assign unused_passthrough = &{ 1'b0, i_clk, i_reset };
+ // }}}
+ // Verilator lint_on UNUSED
+ // }}}
+ end else begin : LOGIC
+ // We'll start with skid buffer itself
+ // {{{
+ reg r_valid;
+ reg [DW-1:0] r_data;
+
+ // r_valid
+ // {{{
+ initial if (OPT_INITIAL) r_valid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_valid <= 0;
+ else if ((i_valid && o_ready) && (o_valid && !i_ready))
+ // We have incoming data, but the output is stalled
+ r_valid <= 1;
+ else if (i_ready)
+ r_valid <= 0;
+ // }}}
+
+ // r_data
+ // {{{
+ initial if (OPT_INITIAL) r_data = 0;
+ always @(posedge i_clk)
+ if (OPT_LOWPOWER && i_reset)
+ r_data <= 0;
+ else if (OPT_LOWPOWER && (!o_valid || i_ready))
+ r_data <= 0;
+ else if ((!OPT_LOWPOWER || !OPT_OUTREG || i_valid) && o_ready)
+ r_data <= i_data;
+
+ assign w_data = r_data;
+ // }}}
+
+ // o_ready
+ // {{{
+ assign o_ready = !r_valid;
+ // }}}
+
+ //
+ // And then move on to the output port
+ //
+ if (!OPT_OUTREG)
+ begin : NET_OUTPUT
+ // Outputs are combinatorially determined from inputs
+ // {{{
+ // o_valid
+ // {{{
+ assign o_valid = !i_reset && (i_valid || r_valid);
+ // }}}
+
+ // o_data
+ // {{{
+ always @(*)
+ if (r_valid)
+ o_data = r_data;
+ else if (!OPT_LOWPOWER || i_valid)
+ o_data = i_data;
+ else
+ o_data = 0;
+ // }}}
+ // }}}
+ end else begin : REG_OUTPUT
+ // Register our outputs
+ // {{{
+ // o_valid
+ // {{{
+ reg ro_valid;
+
+ initial if (OPT_INITIAL) ro_valid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ ro_valid <= 0;
+ else if (!o_valid || i_ready)
+ ro_valid <= (i_valid || r_valid);
+
+ assign o_valid = ro_valid;
+ // }}}
+
+ // o_data
+ // {{{
+ initial if (OPT_INITIAL) o_data = 0;
+ always @(posedge i_clk)
+ if (OPT_LOWPOWER && i_reset)
+ o_data <= 0;
+ else if (!o_valid || i_ready)
+ begin
+
+ if (r_valid)
+ o_data <= r_data;
+ else if (!OPT_LOWPOWER || i_valid)
+ o_data <= i_data;
+ else
+ o_data <= 0;
+ end
+ // }}}
+
+ // }}}
+ end
+ // }}}
+ end endgenerate
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, w_data };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+`ifdef SKIDBUFFER
+`define ASSUME assume
+`else
+`define ASSUME assert
+`endif
+
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming stream properties / assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ always @(posedge i_clk)
+ if (!f_past_valid)
+ begin
+ `ASSUME(!i_valid || !OPT_INITIAL);
+ end else if ($past(i_valid && !o_ready && !i_reset) && !i_reset)
+ `ASSUME(i_valid && $stable(i_data));
+
+`ifdef VERIFIC
+`define FORMAL_VERIFIC
+ // Reset properties
+ property RESET_CLEARS_IVALID;
+ @(posedge i_clk) i_reset |=> !i_valid;
+ endproperty
+
+ property IDATA_HELD_WHEN_NOT_READY;
+ @(posedge i_clk) disable iff (i_reset)
+ i_valid && !o_ready |=> i_valid && $stable(i_data);
+ endproperty
+
+`ifdef SKIDBUFFER
+ assume property (IDATA_HELD_WHEN_NOT_READY);
+`else
+ assert property (IDATA_HELD_WHEN_NOT_READY);
+`endif
+`endif
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing stream properties / assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+
+ generate if (!OPT_PASSTHROUGH)
+ begin
+
+ always @(posedge i_clk)
+ if (!f_past_valid) // || $past(i_reset))
+ begin
+ // Following any reset, valid must be deasserted
+ assert(!o_valid || !OPT_INITIAL);
+ end else if ($past(o_valid && !i_ready && !i_reset) && !i_reset)
+ // Following any stall, valid must remain high and
+ // data must be preserved
+ assert(o_valid && $stable(o_data));
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Other properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (!OPT_PASSTHROUGH)
+ begin
+ // Rule #1:
+ // If registered, then following any reset we should be
+ // ready for a new request
+ // {{{
+ always @(posedge i_clk)
+ if (f_past_valid && $past(OPT_OUTREG && i_reset))
+ assert(o_ready);
+ // }}}
+
+ // Rule #2:
+ // All incoming data must either go directly to the
+ // output port, or into the skid buffer
+ // {{{
+`ifndef VERIFIC
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && $past(i_valid && o_ready
+ && (!OPT_OUTREG || o_valid) && !i_ready))
+ assert(!o_ready && w_data == $past(i_data));
+`else
+ assert property (@(posedge i_clk)
+ disable iff (i_reset)
+ (i_valid && o_ready
+ && (!OPT_OUTREG || o_valid) && !i_ready)
+ |=> (!o_ready && w_data == $past(i_data)));
+`endif
+ // }}}
+
+ // Rule #3:
+ // After the last transaction, o_valid should become idle
+ // {{{
+ if (!OPT_OUTREG)
+ begin
+ // {{{
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && !i_reset
+ && $past(i_ready))
+ begin
+ assert(o_valid == i_valid);
+ assert(!i_valid || (o_data == i_data));
+ end
+ // }}}
+ end else begin
+ // {{{
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset))
+ begin
+ if ($past(i_valid && o_ready))
+ assert(o_valid);
+
+ if ($past(!i_valid && o_ready && i_ready))
+ assert(!o_valid);
+ end
+ // }}}
+ end
+ // }}}
+
+ // Rule #4
+ // Same thing, but this time for o_ready
+ // {{{
+ always @(posedge i_clk)
+ if (f_past_valid && $past(!o_ready && i_ready))
+ assert(o_ready);
+ // }}}
+
+ // If OPT_LOWPOWER is set, o_data and w_data both need to be
+ // zero any time !o_valid or !r_valid respectively
+ // {{{
+ if (OPT_LOWPOWER)
+ begin
+ always @(*)
+ if ((OPT_OUTREG || !i_reset) && !o_valid)
+ assert(o_data == 0);
+
+ always @(*)
+ if (o_ready)
+ assert(w_data == 0);
+
+ end
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`ifdef SKIDBUFFER
+ generate if (!OPT_PASSTHROUGH)
+ begin
+ reg f_changed_data;
+
+ initial f_changed_data = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ f_changed_data <= 1;
+ else if (i_valid && $past(!i_valid || o_ready))
+ begin
+ if (i_data != $past(i_data + 1))
+ f_changed_data <= 0;
+ end else if (!i_valid && i_data != 0)
+ f_changed_data <= 0;
+
+
+`ifndef VERIFIC
+ reg [3:0] cvr_steps, cvr_hold;
+
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ cvr_steps <= 0;
+ cvr_hold <= 0;
+ end else begin
+ cvr_steps <= cvr_steps + 1;
+ cvr_hold <= cvr_hold + 1;
+ case(cvr_steps)
+ 0: if (o_valid || i_valid)
+ cvr_steps <= 0;
+ 1: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 2: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 3: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 4: if (!i_valid || i_ready)
+ cvr_steps <= 0;
+ 5: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 6: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 7: if (!i_valid || i_ready)
+ cvr_steps <= 0;
+ 8: if (!i_valid || i_ready)
+ cvr_steps <= 0;
+ 9: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 10: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 11: if (!i_valid || !i_ready)
+ cvr_steps <= 0;
+ 12: begin
+ cvr_steps <= cvr_steps;
+ cover(!o_valid && !i_valid && f_changed_data);
+ if (!o_valid || !i_ready)
+ cvr_steps <= 0;
+ else
+ cvr_hold <= cvr_hold + 1;
+ end
+ default: assert(0);
+ endcase
+ end
+
+`else
+ // Cover test
+ cover property (@(posedge i_clk)
+ disable iff (i_reset)
+ (!o_valid && !i_valid)
+ ##1 i_valid && i_ready [*3]
+ ##1 i_valid && !i_ready
+ ##1 i_valid && i_ready [*2]
+ ##1 i_valid && !i_ready [*2]
+ ##1 i_valid && i_ready [*3]
+ // Wait for the design to clear
+ ##1 o_valid && i_ready [*0:5]
+ ##1 (!o_valid && !i_valid && f_changed_data));
+`endif
+ end endgenerate
+`endif // SKIDBUFFER
+ // }}}
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/wbarbiter.v b/rtl/wb2axip/wbarbiter.v
new file mode 100644
index 0000000..e068278
--- /dev/null
+++ b/rtl/wb2axip/wbarbiter.v
@@ -0,0 +1,404 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbarbiter.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: This is a priority bus arbiter. It allows two separate wishbone
+// masters to connect to the same bus, while also guaranteeing
+// that the last master can have the bus with no delay any time it is
+// idle. The goal is to minimize the combinatorial logic required in this
+// process, while still minimizing access time.
+//
+// The core logic works like this:
+//
+// 1. If 'A' or 'B' asserts the o_cyc line, a bus cycle will begin,
+// with acccess granted to whomever requested it.
+// 2. If both 'A' and 'B' assert o_cyc at the same time, only 'A'
+// will be granted the bus. (If the alternating parameter
+// is set, A and B will alternate who gets the bus in
+// this case.)
+// 3. The bus will remain owned by whomever the bus was granted to
+// until they deassert the o_cyc line.
+// 4. At the end of a bus cycle, o_cyc is guaranteed to be
+// deasserted (low) for one clock.
+// 5. On the next clock, bus arbitration takes place again. If
+// 'A' requests the bus, no matter how long 'B' was
+// waiting, 'A' will then be granted the bus. (Unless
+// again the alternating parameter is set, then the
+// access is guaranteed to switch to B.)
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2015-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+//
+`define WBA_ALTERNATING
+// }}}
+module wbarbiter #(
+ // {{{
+ parameter DW=32, AW=32,
+ parameter SCHEME="ALTERNATING",
+ parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0,
+ parameter [31:0] F_MAX_STALL = 3,
+ parameter [31:0] F_MAX_ACK_DELAY = 3,
+ parameter [31:0] F_LGDEPTH=3
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ // Bus A
+ // {{{
+ input wire i_a_cyc, i_a_stb, i_a_we,
+ input wire [(AW-1):0] i_a_adr,
+ input wire [(DW-1):0] i_a_dat,
+ input wire [(DW/8-1):0] i_a_sel,
+ output wire o_a_ack, o_a_stall, o_a_err,
+ // }}}
+ // Bus B
+ // {{{
+ input wire i_b_cyc, i_b_stb, i_b_we,
+ input wire [(AW-1):0] i_b_adr,
+ input wire [(DW-1):0] i_b_dat,
+ input wire [(DW/8-1):0] i_b_sel,
+ output wire o_b_ack, o_b_stall, o_b_err,
+ // }}}
+ // Combined/arbitrated bus
+ // {{{
+ output wire o_cyc, o_stb, o_we,
+ output wire [(AW-1):0] o_adr,
+ output wire [(DW-1):0] o_dat,
+ output wire [(DW/8-1):0] o_sel,
+ input wire i_ack, i_stall, i_err
+ // }}}
+`ifdef FORMAL
+ // {{{
+ ,
+ output wire [(F_LGDEPTH-1):0]
+ f_nreqs, f_nacks, f_outstanding,
+ f_a_nreqs, f_a_nacks, f_a_outstanding,
+ f_b_nreqs, f_b_nacks, f_b_outstanding
+ // }}}
+`endif
+ // }}}
+ );
+ //
+
+ // Go high immediately (new cycle) if ...
+ // Previous cycle was low and *someone* is requesting a bus cycle
+ // Go low immadiately if ...
+ // We were just high and the owner no longer wants the bus
+ // WISHBONE Spec recommends no logic between a FF and the o_cyc
+ // This violates that spec. (Rec 3.15, p35)
+ reg r_a_owner;
+
+ assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc;
+ initial r_a_owner = 1'b1;
+
+ generate if (SCHEME == "PRIORITY")
+ begin : PRI
+
+ always @(posedge i_clk)
+ if (!i_b_cyc)
+ r_a_owner <= 1'b1;
+ // Allow B to set its CYC line w/o activating this
+ // interface
+ else if ((i_b_stb)&&(!i_a_cyc))
+ r_a_owner <= 1'b0;
+
+ end else if (SCHEME == "ALTERNATING")
+ begin : ALT
+
+ reg last_owner;
+ initial last_owner = 1'b0;
+ always @(posedge i_clk)
+ if ((i_a_cyc)&&(r_a_owner))
+ last_owner <= 1'b1;
+ else if ((i_b_cyc)&&(!r_a_owner))
+ last_owner <= 1'b0;
+
+ always @(posedge i_clk)
+ if ((!i_a_cyc)&&(!i_b_cyc))
+ r_a_owner <= !last_owner;
+ else if ((r_a_owner)&&(!i_a_cyc))
+ begin
+
+ if (i_b_stb)
+ r_a_owner <= 1'b0;
+
+ end else if ((!r_a_owner)&&(!i_b_cyc))
+ begin
+
+ if (i_a_stb)
+ r_a_owner <= 1'b1;
+
+ end
+
+ end else // if (SCHEME == "LAST")
+ begin : LST
+ always @(posedge i_clk)
+ if ((!i_a_cyc)&&(i_b_stb))
+ r_a_owner <= 1'b0;
+ else if ((!i_b_cyc)&&(i_a_stb))
+ r_a_owner <= 1'b1;
+ end endgenerate
+
+
+ // Realistically, if neither master owns the bus, the output is a
+ // don't care. Thus we trigger off whether or not 'A' owns the bus.
+ // If 'B' owns it all we care is that 'A' does not. Likewise, if
+ // neither owns the bus than the values on the various lines are
+ // irrelevant.
+ assign o_we = (r_a_owner) ? i_a_we : i_b_we;
+
+ generate if (OPT_ZERO_ON_IDLE)
+ begin : ZERO_IDLE
+ // {{{
+ //
+ // OPT_ZERO_ON_IDLE will use up more logic and may even slow
+ // down the master clock if set. However, it may also reduce
+ // the power used by the FPGA by preventing things from toggling
+ // when the bus isn't in use. The option is here because it
+ // also makes it a lot easier to look for when things happen
+ // on the bus via VERILATOR when timing and logic counts
+ // don't matter.
+ //
+ assign o_stb = (o_cyc)? ((r_a_owner) ? i_a_stb : i_b_stb):0;
+ assign o_adr = (o_stb)? ((r_a_owner) ? i_a_adr : i_b_adr):0;
+ assign o_dat = (o_stb)? ((r_a_owner) ? i_a_dat : i_b_dat):0;
+ assign o_sel = (o_stb)? ((r_a_owner) ? i_a_sel : i_b_sel):0;
+ assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0;
+ assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0;
+ assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1;
+ assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1;
+ assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0;
+ assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0;
+ // }}}
+ end else begin : LOW_LOGIC
+ // {{{
+ assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb;
+ assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr;
+ assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat;
+ assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel;
+
+ // We cannot allow the return acknowledgement to ever go high if
+ // the master in question does not own the bus. Hence we force
+ // it low if the particular master doesn't own the bus.
+ assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0;
+ assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0;
+
+ // Stall must be asserted on the same cycle the input master
+ // asserts the bus, if the bus isn't granted to him.
+ assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1;
+ assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1;
+
+ //
+ //
+ assign o_a_err = ( r_a_owner) ? i_err : 1'b0;
+ assign o_b_err = (!r_a_owner) ? i_err : 1'b0;
+ // }}}
+ end endgenerate
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, i_reset, F_LGDEPTH, F_MAX_STALL,
+ F_MAX_ACK_DELAY };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+
+`ifdef WBARBITER
+
+`define ASSUME assume
+`else
+`define ASSUME assert
+`endif
+ reg f_prior_a_ack, f_prior_b_ack;
+
+
+ reg f_past_valid;
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ initial `ASSUME(!i_a_cyc);
+ initial `ASSUME(!i_a_stb);
+
+ initial `ASSUME(!i_b_cyc);
+ initial `ASSUME(!i_b_stb);
+
+ initial `ASSUME(!i_ack);
+ initial `ASSUME(!i_err);
+
+ always @(*)
+ if (!f_past_valid)
+ `ASSUME(i_reset);
+
+ always @(posedge i_clk)
+ begin
+ if (o_cyc)
+ assert((i_a_cyc)||(i_b_cyc));
+ if ((f_past_valid)&&($past(o_cyc))&&(o_cyc))
+ assert($past(r_a_owner) == r_a_owner);
+ end
+
+ fwb_master #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(F_MAX_STALL),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(F_MAX_ACK_DELAY),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wbm(
+ // {{{
+ i_clk, i_reset,
+ o_cyc, o_stb, o_we, o_adr, o_dat, o_sel,
+ i_ack, i_stall, 32'h0, i_err,
+ f_nreqs, f_nacks, f_outstanding
+ // }}}
+ );
+
+ fwb_slave #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(0),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wba(
+ // {{{
+ i_clk, i_reset,
+ i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel,
+ o_a_ack, o_a_stall, 32'h0, o_a_err,
+ f_a_nreqs, f_a_nacks, f_a_outstanding
+ // }}}
+ );
+
+ fwb_slave #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(0),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wbb(
+ // {{{
+ i_clk, i_reset,
+ i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel,
+ o_b_ack, o_b_stall, 32'h0, o_b_err,
+ f_b_nreqs, f_b_nacks, f_b_outstanding
+ // }}}
+ );
+
+ always @(posedge i_clk)
+ if (r_a_owner)
+ begin
+ assert(f_b_nreqs == 0);
+ assert(f_b_nacks == 0);
+ assert(f_a_outstanding == f_outstanding);
+ end else begin
+ assert(f_a_nreqs == 0);
+ assert(f_a_nacks == 0);
+ assert(f_b_outstanding == f_outstanding);
+ end
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&($past(i_a_stb))&&(!$past(i_b_cyc)))
+ assert(r_a_owner);
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&(!$past(i_a_cyc))&&($past(i_b_stb)))
+ assert(!r_a_owner);
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(r_a_owner != $past(r_a_owner)))
+ assert(!$past(o_cyc));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial f_prior_a_ack = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(o_a_err)||(o_b_err))
+ f_prior_a_ack <= 1'b0;
+ else if ((o_cyc)&&(o_a_ack))
+ f_prior_a_ack <= 1'b1;
+
+ initial f_prior_b_ack = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(o_a_err)||(o_b_err))
+ f_prior_b_ack <= 1'b0;
+ else if ((o_cyc)&&(o_b_ack))
+ f_prior_b_ack <= 1'b1;
+
+ always @(posedge i_clk)
+ begin
+ cover(f_prior_b_ack && o_cyc && o_a_ack);
+
+ cover((o_cyc && o_a_ack)
+ &&($past(o_cyc && o_a_ack))
+ &&($past(o_cyc && o_a_ack,2)));
+
+
+ cover(f_prior_a_ack && o_cyc && o_b_ack);
+
+ cover((o_cyc && o_b_ack)
+ &&($past(o_cyc && o_b_ack))
+ &&($past(o_cyc && o_b_ack,2)));
+ end
+
+ always @(*)
+ cover(o_cyc && o_b_ack);
+ // }}}
+// }}}
+`endif
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/wbc2pipeline.v b/rtl/wb2axip/wbc2pipeline.v
new file mode 100644
index 0000000..ad6a11c
--- /dev/null
+++ b/rtl/wb2axip/wbc2pipeline.v
@@ -0,0 +1,188 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbc2pipeline.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Takes a WB classic connection from a master, and converts it
+// to WB pipelined for the slave.
+//
+// If you don't see an `ifdef FORMAL section below, then this core hasn't
+// (yet) been formally verified. Use it at your own risk.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbc2pipeline #(
+ // {{{
+ parameter AW = 12,
+ DW = 32
+ // }}}
+ ) (
+ // {{{
+ //
+ input wire i_clk, i_reset,
+ //
+ // Incoming WB classic port
+ input wire i_scyc, i_sstb, i_swe,
+ input wire [AW-1:0] i_saddr,
+ input wire [DW-1:0] i_sdata,
+ input wire [DW/8-1:0] i_ssel,
+ output reg o_sack,
+ output reg [DW-1:0] o_sdata,
+ output reg o_serr,
+ input wire [3-1:0] i_scti,
+ input wire [2-1:0] i_sbte,
+ //
+ // Outgoing WB pipelined port
+ output reg o_mcyc, o_mstb, o_mwe,
+ output reg [AW-1:0] o_maddr,
+ output reg [DW-1:0] o_mdata,
+ output reg [DW/8-1:0] o_msel,
+ input wire i_mstall,
+ input wire i_mack,
+ input wire [DW-1:0] i_mdata,
+ input wire i_merr
+ // }}}
+ );
+
+ reg last_stb;
+
+ // last_stb
+ // {{{
+ initial last_stb = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_sstb || o_sack || o_serr)
+ last_stb <= 0;
+ else if (!i_mstall)
+ last_stb <= 1;
+ // }}}
+
+ // Combinatorial assignments to the downstream port
+ // {{{
+ always @(*)
+ begin
+ o_mcyc = i_scyc && (!o_serr);
+ o_mstb = i_sstb & !last_stb && (!o_serr);
+ o_mwe = i_swe;
+ o_maddr = i_saddr;
+ o_mdata = i_sdata;
+ o_msel = i_ssel;
+ end
+ // }}}
+
+ // o_sack, o_serr
+ // {{{
+ initial o_sack = 0;
+ initial o_serr = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_sstb || o_sack || o_serr)
+ begin
+ o_sack <= 0;
+ o_serr <= 0;
+ end else begin
+ if (i_mack)
+ o_sack <= 1;
+ if (i_merr)
+ o_serr <= 1;
+ end
+ // }}}
+
+ // o_sdata
+ // {{{
+ always @(posedge i_clk)
+ if (i_mack)
+ o_sdata <= i_mdata;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire [4:0] unused;
+ assign unused = { i_scti, i_sbte };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_LGDEPTH = 1;
+ reg [F_LGDEPTH-1:0] f_nreqs, f_nacks, f_outstanding;
+
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid = 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Upstream Wishbone classic slave properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ fwbc_slave #(.AW(AW), .DW(DW)) incoming (i_clk, i_reset,
+ i_scyc, i_sstb, i_swe, i_saddr, i_sdata, i_ssel,
+ i_scti, i_sbte,
+ o_sack, o_sdata, o_serr, 1'b0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Downstream Wishbone pipeline slave properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ fwb_master #(.AW(AW), .DW(DW),
+ .F_MAX_STALL(3),
+ .F_MAX_ACK_DELAY(3),
+ .F_LGDEPTH(1)) pipelined (i_clk, i_reset,
+ o_mcyc, o_mstb, o_mwe, o_maddr, o_mdata, o_msel,
+ i_mack, i_mstall, i_mdata, i_merr,
+ f_nreqs, f_nacks, f_outstanding);
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/wbm2axilite.v b/rtl/wb2axip/wbm2axilite.v
new file mode 100644
index 0000000..6cda44d
--- /dev/null
+++ b/rtl/wb2axip/wbm2axilite.v
@@ -0,0 +1,685 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbm2axilite.v (Wishbone master to AXI slave, pipelined)
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Convert from a wishbone master to an AXI lite interface. The
+// big difference is that AXI lite doesn't support bursting,
+// or transaction ID's. This actually makes the task a *LOT* easier.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2018-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbm2axilite #(
+ // {{{
+ parameter C_AXI_ADDR_WIDTH = 28,// AXI Address width
+ localparam C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
+ localparam DW = C_AXI_DATA_WIDTH,// Wishbone data width
+ localparam AW = C_AXI_ADDR_WIDTH-2// WB addr width (log wordsize)
+ // }}}
+ ) (
+ // {{{
+ // We'll share the clock and the reset
+ input wire i_clk,
+ input wire i_reset,
+ // Wishbone
+ // {{{
+ input wire i_wb_cyc,
+ input wire i_wb_stb,
+ input wire i_wb_we,
+ input wire [(AW-1):0] i_wb_addr,
+ input wire [(DW-1):0] i_wb_data,
+ input wire [(DW/8-1):0] i_wb_sel,
+ output wire o_wb_stall,
+ output reg o_wb_ack,
+ output reg [(DW-1):0] o_wb_data,
+ output reg o_wb_err,
+ // }}}
+ // AXI-Lite
+ // {{{
+ // AXI write address channel signals
+ output reg o_axi_awvalid,
+ input wire i_axi_awready,
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_awaddr,
+ output wire [2:0] o_axi_awprot,
+ //
+ // AXI write data channel signals
+ output reg o_axi_wvalid,
+ input wire i_axi_wready,
+ output reg [C_AXI_DATA_WIDTH-1:0] o_axi_wdata,
+ output reg [C_AXI_DATA_WIDTH/8-1:0] o_axi_wstrb,
+ //
+ // AXI write response channel signals
+ input wire i_axi_bvalid,
+ output wire o_axi_bready,
+ input wire [1:0] i_axi_bresp,
+ //
+ // AXI read address channel signals
+ output reg o_axi_arvalid,
+ input wire i_axi_arready,
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_araddr,
+ output wire [2:0] o_axi_arprot,
+ //
+ // AXI read data channel signals
+ input wire i_axi_rvalid,
+ output wire o_axi_rready,
+ input wire [C_AXI_DATA_WIDTH-1:0] i_axi_rdata,
+ input wire [1:0] i_axi_rresp
+ // }}}
+ // }}}
+ );
+
+ // Declarations
+ // {{{
+//*****************************************************************************
+// Local Parameter declarations
+//*****************************************************************************
+
+ //
+ // LGIFOFLN: The log (based two) of the size of our FIFO. This is a
+ // localparam since 1) 32-bit distributed memories nearly come for
+ // free, and 2) because there is no performance gain to be had in larger
+ // memories. 2^32 entries is the perfect size for this application.
+ // Any smaller, and the core will not be able to maintain 100%
+ // throughput.
+ localparam LGFIFOLN = 5;
+
+
+//*****************************************************************************
+// Internal register and wire declarations
+//*****************************************************************************
+
+// Things we're not changing ...
+ assign o_axi_awprot = 3'b000; // Unpriviledged, unsecure, data access
+ assign o_axi_arprot = 3'b000; // Unpriviledged, unsecure, data access
+
+ reg full_fifo, err_state, axi_reset_state, wb_we;
+ reg [3:0] reset_count;
+ reg pending;
+ reg [LGFIFOLN-1:0] outstanding, err_pending;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Master bridge logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_wb_stall
+ // {{{
+ assign o_wb_stall = (full_fifo)
+ ||((!i_wb_we)&&( wb_we)&&(pending))
+ ||(( i_wb_we)&&(!wb_we)&&(pending))
+ ||(err_state)||(axi_reset_state)
+ ||(o_axi_arvalid)&&(!i_axi_arready)
+ ||(o_axi_awvalid)&&(!i_axi_awready)
+ ||(o_axi_wvalid)&&(!i_axi_wready);
+ // }}}
+
+ // reset_count, axi_reset_state
+ // {{{
+ initial axi_reset_state = 1'b1;
+ initial reset_count = 4'hf;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ axi_reset_state <= 1'b1;
+ if (reset_count > 0)
+ reset_count <= reset_count - 1'b1;
+ end else if ((axi_reset_state)&&(reset_count > 0))
+ reset_count <= reset_count - 1'b1;
+ else begin
+ axi_reset_state <= 1'b0;
+ reset_count <= 4'hf;
+ end
+ // }}}
+
+ // pending, outstanding, full_fifo: Count outstanding transactions
+ // {{{
+ initial pending = 0;
+ initial outstanding = 0;
+ always @(posedge i_clk)
+ if ((i_reset)||(axi_reset_state))
+ begin
+ pending <= 0;
+ outstanding <= 0;
+ full_fifo <= 0;
+ end else if ((err_state)||(!i_wb_cyc))
+ begin
+ pending <= 0;
+ outstanding <= 0;
+ full_fifo <= 0;
+ end else case({ ((i_wb_stb)&&(!o_wb_stall)), (o_wb_ack) })
+ 2'b01: begin
+ outstanding <= outstanding - 1'b1;
+ pending <= (outstanding >= 2);
+ full_fifo <= 1'b0;
+ end
+ 2'b10: begin
+ outstanding <= outstanding + 1'b1;
+ pending <= 1'b1;
+ full_fifo <= (outstanding >= {{(LGFIFOLN-2){1'b1}},2'b01});
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ always @(posedge i_clk)
+ if ((i_wb_stb)&&(!o_wb_stall))
+ wb_we <= i_wb_we;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write address logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+
+ // o_axi_awvalid
+ // {{{
+ initial o_axi_awvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_axi_awvalid <= 0;
+ else
+ o_axi_awvalid <= (!o_wb_stall)&&(i_wb_stb)&&(i_wb_we)
+ ||(o_axi_awvalid)&&(!i_axi_awready);
+ // }}}
+
+
+ // o_axi_awaddr
+ // {{{
+ always @(posedge i_clk)
+ if (!o_wb_stall)
+ o_axi_awaddr <= { i_wb_addr, 2'b00 };
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read address logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_axi_arvalid
+ // {{{
+ initial o_axi_arvalid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_axi_arvalid <= 1'b0;
+ else
+ o_axi_arvalid <= (!o_wb_stall)&&(i_wb_stb)&&(!i_wb_we)
+ ||((o_axi_arvalid)&&(!i_axi_arready));
+ // }}}
+
+ // o_axi_araddr
+ // {{{
+ always @(posedge i_clk)
+ if (!o_wb_stall)
+ o_axi_araddr <= { i_wb_addr, 2'b00 };
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write data logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // o_axi_wdata, o_axi_wstrb
+ // {{{
+ always @(posedge i_clk)
+ if (!o_wb_stall)
+ begin
+ o_axi_wdata <= i_wb_data;
+ o_axi_wstrb <= i_wb_sel;
+ end
+ // }}}
+
+ // o_axi_wvalid
+ // {{{
+ initial o_axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_axi_wvalid <= 0;
+ else
+ o_axi_wvalid <= ((!o_wb_stall)&&(i_wb_stb)&&(i_wb_we))
+ ||((o_axi_wvalid)&&(!i_axi_wready));
+ // }}}
+
+ // o_wb_ack
+ // {{{
+ initial o_wb_ack = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(!i_wb_cyc)||(err_state))
+ o_wb_ack <= 1'b0;
+ else if (err_state)
+ o_wb_ack <= 1'b0;
+ else if ((i_axi_bvalid)&&(!i_axi_bresp[1]))
+ o_wb_ack <= 1'b1;
+ else if ((i_axi_rvalid)&&(!i_axi_rresp[1]))
+ o_wb_ack <= 1'b1;
+ else
+ o_wb_ack <= 1'b0;
+ // }}}
+
+ // o_wb_data
+ // {{{
+ always @(posedge i_clk)
+ o_wb_data <= i_axi_rdata;
+ // }}}
+ // }}}
+ // Read data channel / response logic
+ assign o_axi_rready = 1'b1;
+ assign o_axi_bready = 1'b1;
+
+ // o_wb_err
+ // {{{
+ initial o_wb_err = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(!i_wb_cyc)||(err_state))
+ o_wb_err <= 1'b0;
+ else if ((i_axi_bvalid)&&(i_axi_bresp[1]))
+ o_wb_err <= 1'b1;
+ else if ((i_axi_rvalid)&&(i_axi_rresp[1]))
+ o_wb_err <= 1'b1;
+ else
+ o_wb_err <= 1'b0;
+ // }}}
+
+ // err_state
+ // {{{
+ initial err_state = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ err_state <= 0;
+ else if ((i_axi_bvalid)&&(i_axi_bresp[1]))
+ err_state <= 1'b1;
+ else if ((i_axi_rvalid)&&(i_axi_rresp[1]))
+ err_state <= 1'b1;
+ else if ((pending)&&(!i_wb_cyc))
+ err_state <= 1'b1;
+ else if (err_pending == 0)
+ err_state <= 0;
+ // }}}
+
+ // err_pending
+ // {{{
+ initial err_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ err_pending <= 0;
+ else case({ ((i_wb_stb)&&(!o_wb_stall)),
+ ((i_axi_bvalid)||(i_axi_rvalid)) })
+ 2'b01: err_pending <= err_pending - 1'b1;
+ 2'b10: err_pending <= err_pending + 1'b1;
+ default: begin end
+ endcase
+ // }}}
+
+ // Make verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire [2:0] unused;
+ assign unused = { i_wb_cyc, i_axi_bresp[0], i_axi_rresp[0] };
+ // verilator lint_on UNUSED
+ // }}}
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Formal methods section
+// {{{
+// These are only relevant when *proving* that this translator works
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ localparam FIFOLN = (1<<LGFIFOLN);
+ reg f_past_valid;
+//
+`define ASSUME assume
+`define ASSERT assert
+
+ // Parameters
+ initial assert(DW == 32);
+ initial assert(C_AXI_ADDR_WIDTH == AW+2);
+ //
+
+ //
+ // Setup
+ //
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ always @(*)
+ if (!f_past_valid)
+ `ASSUME(i_reset);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Bus properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ assume(f_past_valid || i_reset);
+
+ wire [(LGFIFOLN-1):0] f_wb_nreqs, f_wb_nacks,f_wb_outstanding;
+ fwb_slave #(
+ // {{{
+ .DW(DW),.AW(AW),
+ .F_MAX_STALL(0),
+ .F_MAX_ACK_DELAY(0),
+ .F_LGDEPTH(LGFIFOLN),
+ .F_MAX_REQUESTS(FIFOLN-2)
+ // }}}
+ ) f_wb(
+ // {{{
+ i_clk, i_reset, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr,
+ i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
+ f_wb_nreqs, f_wb_nacks, f_wb_outstanding
+ // }}}
+ );
+
+ wire [(LGFIFOLN-1):0] f_axi_rd_outstanding,
+ f_axi_wr_outstanding,
+ f_axi_awr_outstanding;
+
+ faxil_master #(
+ // {{{
+ // .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(LGFIFOLN),
+ .F_AXI_MAXWAIT(3),
+ .F_AXI_MAXDELAY(3)
+ // }}}
+ ) f_axil(
+ // {{{
+ .i_clk(i_clk),
+ .i_axi_reset_n((!i_reset)&&(!axi_reset_state)),
+ // Write address channel
+ .i_axi_awvalid(o_axi_awvalid),
+ .i_axi_awready(i_axi_awready),
+ .i_axi_awaddr( o_axi_awaddr),
+ .i_axi_awprot( o_axi_awprot),
+ // Write data channel
+ .i_axi_wvalid( o_axi_wvalid),
+ .i_axi_wready( i_axi_wready),
+ .i_axi_wdata( o_axi_wdata),
+ .i_axi_wstrb( o_axi_wstrb),
+ // Write response channel
+ .i_axi_bvalid( i_axi_bvalid),
+ .i_axi_bready( o_axi_bready),
+ .i_axi_bresp( i_axi_bresp),
+ // Read address channel
+ .i_axi_arvalid(o_axi_arvalid),
+ .i_axi_arready(i_axi_arready),
+ .i_axi_araddr( o_axi_araddr),
+ .i_axi_arprot( o_axi_arprot),
+ // Read data channel
+ .i_axi_rvalid( i_axi_rvalid),
+ .i_axi_rready( o_axi_rready),
+ .i_axi_rdata( i_axi_rdata),
+ .i_axi_rresp( i_axi_rresp),
+ // Counts
+ .f_axi_rd_outstanding( f_axi_rd_outstanding),
+ .f_axi_wr_outstanding( f_axi_wr_outstanding),
+ .f_axi_awr_outstanding( f_axi_awr_outstanding)
+ // }}}
+ );
+ // }}}
+
+
+ //////////////////////////////////////////////
+ //
+ //
+ // Assertions about the AXI4 ouputs
+ //
+ //
+ //////////////////////////////////////////////
+
+ // Write response channel
+ always @(posedge i_clk)
+ // We keep bready high, so the other condition doesn't
+ // need to be checked
+ assert(o_axi_bready);
+
+ // AXI read data channel signals
+ always @(posedge i_clk)
+ // We keep o_axi_rready high, so the other condition's
+ // don't need to be checked here
+ assert(o_axi_rready);
+
+ //
+ // Let's look into write requests
+ //
+ initial assert(!o_axi_awvalid);
+ initial assert(!o_axi_wvalid);
+ always @(posedge i_clk)
+ if ((!f_past_valid)||($past(i_reset))||($past(axi_reset_state)))
+ begin
+ assert(!o_axi_awvalid);
+ assert(!o_axi_wvalid);
+ end
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&($past((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall))))
+ begin
+ // Following any write request that we accept, awvalid
+ // and wvalid should both be true
+ assert(o_axi_awvalid);
+ assert(o_axi_wvalid);
+ assert(wb_we);
+ end else if ((f_past_valid)&&($past(i_reset)))
+ begin
+ if ($past(i_axi_awready))
+ assert(!o_axi_awvalid);
+ if ($past(i_axi_wready))
+ assert(!o_axi_wvalid);
+ end
+
+ //
+ // AXI write address channel
+ //
+ always @(posedge i_clk)
+ if ((f_past_valid)&&($past((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall))))
+ assert(o_axi_awaddr == { $past(i_wb_addr[AW-1:0]), 2'b00 });
+
+ //
+ // AXI write data channel
+ //
+ always @(posedge i_clk)
+ if ((f_past_valid)&&($past(i_wb_stb)&&(i_wb_we)&&(!$past(o_wb_stall))))
+ begin
+ assert(o_axi_wdata == $past(i_wb_data));
+ assert(o_axi_wstrb == $past(i_wb_sel));
+ end
+
+ //
+ // AXI read address channel
+ //
+ initial assert(!o_axi_arvalid);
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&($past((i_wb_stb)&&(!i_wb_we)&&(!o_wb_stall))))
+ begin
+ assert(o_axi_arvalid);
+ assert(o_axi_araddr == { $past(i_wb_addr), 2'b00 });
+ end
+ //
+
+ //
+ // AXI write response channel
+ //
+
+ //
+ // AXI read data channel signals
+ //
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(($past(i_reset))||($past(axi_reset_state))))
+ begin
+ // Relate err_pending to outstanding
+ assert(outstanding == 0);
+ assert(err_pending == 0);
+ end else if (!err_state)
+ assert(err_pending == outstanding - ((o_wb_ack)||(o_wb_err)));
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(($past(i_reset))||($past(axi_reset_state))))
+ begin
+ assert(f_axi_awr_outstanding == 0);
+ assert(f_axi_wr_outstanding == 0);
+ assert(f_axi_rd_outstanding == 0);
+
+ assert(f_wb_outstanding == 0);
+ assert(!pending);
+ assert(outstanding == 0);
+ assert(err_pending == 0);
+ end else if (wb_we)
+ begin
+ case({o_axi_awvalid,o_axi_wvalid})
+ 2'b00: begin
+ `ASSERT(f_axi_awr_outstanding == err_pending);
+ `ASSERT(f_axi_wr_outstanding == err_pending);
+ end
+ 2'b01: begin
+ `ASSERT(f_axi_awr_outstanding == err_pending);
+ `ASSERT(f_axi_wr_outstanding +1 == err_pending);
+ end
+ 2'b10: begin
+ `ASSERT(f_axi_awr_outstanding+1 == err_pending);
+ `ASSERT(f_axi_wr_outstanding == err_pending);
+ end
+ 2'b11: begin
+ `ASSERT(f_axi_awr_outstanding+1 == err_pending);
+ `ASSERT(f_axi_wr_outstanding +1 == err_pending);
+ end
+ endcase
+
+ //
+ `ASSERT(!o_axi_arvalid);
+ `ASSERT(f_axi_rd_outstanding == 0);
+ end else begin
+ if (!o_axi_arvalid)
+ `ASSERT(f_axi_rd_outstanding == err_pending);
+ else
+ `ASSERT(f_axi_rd_outstanding+1 == err_pending);
+
+ `ASSERT(!o_axi_awvalid);
+ `ASSERT(!o_axi_wvalid);
+ `ASSERT(f_axi_awr_outstanding == 0);
+ `ASSERT(f_axi_wr_outstanding == 0);
+ end
+
+ always @(*)
+ if ((!i_reset)&&(i_wb_cyc)&&(!err_state))
+ `ASSERT(f_wb_outstanding == outstanding);
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(err_state))
+ `ASSERT((o_wb_err)||(f_wb_outstanding == 0));
+
+ always @(posedge i_clk)
+ `ASSERT(pending == (outstanding != 0));
+ //
+ // Make sure we only create one request at a time
+ always @(posedge i_clk)
+ `ASSERT((!o_axi_arvalid)||(!o_axi_wvalid));
+ always @(posedge i_clk)
+ `ASSERT((!o_axi_arvalid)||(!o_axi_awvalid));
+ always @(posedge i_clk)
+ if (wb_we)
+ `ASSERT(!o_axi_arvalid);
+ else
+ `ASSERT((!o_axi_awvalid)&&(!o_axi_wvalid));
+
+ always @(*)
+ if (&outstanding[LGFIFOLN-1:1])
+ `ASSERT(full_fifo);
+ always @(*)
+ assert(outstanding < {(LGFIFOLN){1'b1}});
+
+ // AXI cover results
+ always @(*)
+ cover(i_axi_bvalid && o_axi_bready);
+ always @(*)
+ cover(i_axi_rvalid && o_axi_rready);
+
+ always @(posedge i_clk)
+ cover(i_axi_bvalid && o_axi_bready
+ && $past(i_axi_bvalid && o_axi_bready)
+ && $past(i_axi_bvalid && o_axi_bready,2));
+
+ always @(posedge i_clk)
+ cover(i_axi_rvalid && o_axi_rready
+ && $past(i_axi_rvalid && o_axi_rready)
+ && $past(i_axi_rvalid && o_axi_rready,2));
+
+ // AXI cover requests
+ always @(posedge i_clk)
+ cover(o_axi_arvalid && i_axi_arready
+ && $past(o_axi_arvalid && i_axi_arready)
+ && $past(o_axi_arvalid && i_axi_arready,2));
+
+ always @(posedge i_clk)
+ cover(o_axi_awvalid && i_axi_awready
+ && $past(o_axi_awvalid && i_axi_awready)
+ && $past(o_axi_awvalid && i_axi_awready,2));
+
+ always @(posedge i_clk)
+ cover(o_axi_wvalid && i_axi_wready
+ && $past(o_axi_wvalid && i_axi_wready)
+ && $past(o_axi_wvalid && i_axi_wready,2));
+
+ always @(*)
+ cover(i_axi_rvalid && o_axi_rready);
+
+ // Wishbone cover results
+ always @(*)
+ cover(i_wb_cyc && o_wb_ack);
+
+ always @(posedge i_clk)
+ cover(i_wb_cyc && o_wb_ack
+ && $past(o_wb_ack)&&$past(o_wb_ack,2));
+
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/wbm2axisp.v b/rtl/wb2axip/wbm2axisp.v
new file mode 100644
index 0000000..9c7909a
--- /dev/null
+++ b/rtl/wb2axip/wbm2axisp.v
@@ -0,0 +1,1155 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbm2axisp.v (Wishbone master to AXI slave, pipelined)
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: The B4 Wishbone SPEC allows transactions at a speed as fast as
+// one per clock. The AXI bus allows transactions at a speed of
+// one read and one write transaction per clock. These capabilities work
+// by allowing requests to take place prior to responses, such that the
+// requests might go out at once per clock and take several clocks, and
+// the responses may start coming back several clocks later. In other
+// words, both protocols allow multiple transactions to be "in flight" at
+// the same time. Current wishbone to AXI converters, however, handle only
+// one transaction at a time: initiating the transaction, and then waiting
+// for the transaction to complete before initiating the next.
+//
+// The purpose of this core is to maintain the speed of both buses, while
+// transiting from the Wishbone (as master) to the AXI bus (as slave) and
+// back again.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbm2axisp #(
+ // {{{
+ parameter C_AXI_DATA_WIDTH = 128,// Width of the AXI R&W data
+ parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width (log wordsize)
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter DW = 32, // Wishbone data width
+ parameter AW = 26, // Wishbone address width (log wordsize)
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_WRITE_ID = 1'b0,
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_READ_ID = 1'b1,
+ //
+ // OPT_LITTLE_ENDIAN controls which word has the greatest address
+ // when the bus size is adjusted. If OPT_LITTLE_ENDIAN is true,
+ // the biggest address is in the most significant word(s), otherwise
+ // the least significant word(s). This parameter is ignored if
+ // C_AXI_DATA_WIDTH == DW.
+ parameter [0:0] OPT_LITTLE_ENDIAN = 1'b1,
+ parameter LGFIFO = 6
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, // System clock
+ input wire i_reset,// Reset signal,drives AXI rst
+
+ // AXI write address channel signals
+ output reg o_axi_awvalid, // Write address valid
+ input wire i_axi_awready, // Slave is ready to accept
+ output wire [C_AXI_ID_WIDTH-1:0] o_axi_awid, // Write ID
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_awaddr, // Write address
+ output wire [7:0] o_axi_awlen, // Write Burst Length
+ output wire [2:0] o_axi_awsize, // Write Burst size
+ output wire [1:0] o_axi_awburst, // Write Burst type
+ output wire [0:0] o_axi_awlock, // Write lock type
+ output wire [3:0] o_axi_awcache, // Write Cache type
+ output wire [2:0] o_axi_awprot, // Write Protection type
+ output wire [3:0] o_axi_awqos, // Write Quality of Svc
+
+// AXI write data channel signals
+ output reg o_axi_wvalid, // Write valid
+ input wire i_axi_wready, // Write data ready
+ output reg [C_AXI_DATA_WIDTH-1:0] o_axi_wdata, // Write data
+ output reg [C_AXI_DATA_WIDTH/8-1:0] o_axi_wstrb, // Write strobes
+ output wire o_axi_wlast, // Last write transaction
+
+// AXI write response channel signals
+ input wire i_axi_bvalid, // Write reponse valid
+ output wire o_axi_bready, // Response ready
+ input wire [C_AXI_ID_WIDTH-1:0] i_axi_bid, // Response ID
+ input wire [1:0] i_axi_bresp, // Write response
+
+// AXI read address channel signals
+ output reg o_axi_arvalid, // Read address valid
+ input wire i_axi_arready, // Read address ready
+ output wire [C_AXI_ID_WIDTH-1:0] o_axi_arid, // Read ID
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_araddr, // Read address
+ output wire [7:0] o_axi_arlen, // Read Burst Length
+ output wire [2:0] o_axi_arsize, // Read Burst size
+ output wire [1:0] o_axi_arburst, // Read Burst type
+ output wire [0:0] o_axi_arlock, // Read lock type
+ output wire [3:0] o_axi_arcache, // Read Cache type
+ output wire [2:0] o_axi_arprot, // Read Protection type
+ output wire [3:0] o_axi_arqos, // Read Protection type
+
+// AXI read data channel signals
+ input wire i_axi_rvalid, // Read reponse valid
+ output wire o_axi_rready, // Read Response ready
+ input wire [C_AXI_ID_WIDTH-1:0] i_axi_rid, // Response ID
+ input wire [C_AXI_DATA_WIDTH-1:0] i_axi_rdata, // Read data
+ input wire [1:0] i_axi_rresp, // Read response
+ input wire i_axi_rlast, // Read last
+
+ // We'll share the clock and the reset
+ input wire i_wb_cyc,
+ input wire i_wb_stb,
+ input wire i_wb_we,
+ input wire [(AW-1):0] i_wb_addr,
+ input wire [(DW-1):0] i_wb_data,
+ input wire [(DW/8-1):0] i_wb_sel,
+ output reg o_wb_stall,
+ output reg o_wb_ack,
+ output reg [(DW-1):0] o_wb_data,
+ output reg o_wb_err
+ // }}}
+);
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Localparameter declarations, initial parameter consistency check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam LG_AXI_DW = $clog2(C_AXI_DATA_WIDTH);
+ localparam LG_WB_DW = $clog2(DW);
+ // localparam FIFOLN = (1<<LGFIFO);
+ localparam SUBW = LG_AXI_DW-LG_WB_DW;
+
+ // The various address widths must be properly related. We'll insist
+ // upon that relationship here.
+ initial begin
+ // This design can't (currently) handle WB widths wider than
+ // the AXI width it is driving. It can only handle widths
+ // mismatches in the other direction
+ if (C_AXI_DATA_WIDTH < DW)
+ $stop;
+ if (DW == 8 && AW != C_AXI_ADDR_WIDTH)
+ $stop;
+
+ // There must be a definitive relationship between the address
+ // widths of the AXI and WB, and that width is dependent upon
+ // the WB data width
+ if (C_AXI_ADDR_WIDTH != AW + $clog2(DW)-3)
+ $stop;
+ if ( (C_AXI_DATA_WIDTH / DW !=32)
+ &&(C_AXI_DATA_WIDTH / DW !=16)
+ &&(C_AXI_DATA_WIDTH / DW != 8)
+ &&(C_AXI_DATA_WIDTH / DW != 4)
+ &&(C_AXI_DATA_WIDTH / DW != 2)
+ &&(C_AXI_DATA_WIDTH != DW))
+ $stop;
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal register and wire declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Things we're not changing ...
+ localparam DWSIZE = $clog2(DW)-3;
+ assign o_axi_awid = AXI_WRITE_ID;
+ assign o_axi_awlen = 8'h0; // Burst length is one
+ assign o_axi_awsize = DWSIZE[2:0];
+ assign o_axi_wlast = 1;
+ assign o_axi_awburst = 2'b01; // Incrementing address (ignored)
+ assign o_axi_awlock = 1'b0; // Normal signaling
+ assign o_axi_arlock = 1'b0; // Normal signaling
+ assign o_axi_awcache = 4'h3; // Normal: no cache, modifiable
+ //
+ assign o_axi_arid = AXI_READ_ID;
+ assign o_axi_arlen = 8'h0; // Burst length is one
+ assign o_axi_arsize = DWSIZE[2:0];
+ assign o_axi_arburst = 2'b01; // Incrementing address (ignored)
+ assign o_axi_arcache = 4'h3; // Normal: no cache, modifiable
+ assign o_axi_awprot = 3'b010; // Unpriviledged, unsecure, data access
+ assign o_axi_arprot = 3'b010; // Unpriviledged, unsecure, data access
+ assign o_axi_awqos = 4'h0; // Lowest quality of service (unused)
+ assign o_axi_arqos = 4'h0; // Lowest quality of service (unused)
+
+ reg direction, full, empty, flushing, nearfull;
+ reg [LGFIFO:0] npending;
+ //
+ wire skid_ready, m_valid, m_we;
+ reg m_ready;
+ wire [AW-1:0] m_addr;
+ wire [DW-1:0] m_data;
+ wire [DW/8-1:0] m_sel;
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Overarching command logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial direction = 0;
+ always @(posedge i_clk)
+ if (empty)
+ direction <= m_we;
+
+ initial npending = 0;
+ initial empty = 1;
+ initial full = 0;
+ initial nearfull = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ npending <= 0;
+ empty <= 1;
+ full <= 0;
+ nearfull <= 0;
+ end else case ({m_valid && m_ready, i_axi_bvalid||i_axi_rvalid})
+ 2'b10: begin
+ npending <= npending + 1;
+ empty <= 0;
+ nearfull <= &(npending[LGFIFO-1:1]);
+ full <= &(npending[LGFIFO-1:0]);
+ end
+ 2'b01: begin
+ nearfull <= full;
+ npending <= npending - 1;
+ empty <= (npending == 1);
+ full <= 0;
+ end
+ default: begin end
+ endcase
+
+ initial flushing = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ flushing <= 0;
+ else if ((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1])
+ ||(!i_wb_cyc && !empty))
+ flushing <= 1'b1;
+ else if (empty)
+ flushing <= 1'b0;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Wishbone input skidbuffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ skidbuffer #(
+ // {{{
+ .DW(1+AW+DW+(DW/8)),
+ .OPT_OUTREG(1'b0)
+ // }}}
+ ) skid (
+ // {{{
+ .i_clk(i_clk), .i_reset(i_reset || !i_wb_cyc),
+ .i_valid(i_wb_stb), .o_ready(skid_ready),
+ .i_data({ i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }),
+ .o_valid(m_valid), .i_ready(m_ready),
+ .o_data({ m_we, m_addr, m_data, m_sel })
+ // }}}
+ );
+
+ always @(*)
+ o_wb_stall = !skid_ready;
+
+ always @(*)
+ begin
+ m_ready = 1;
+
+ if (flushing || nearfull || ((m_we != direction)&&(!empty)))
+ m_ready = 1'b0;
+ if (o_axi_awvalid && !i_axi_awready)
+ m_ready = 1'b0;
+ if (o_axi_wvalid && !i_axi_wready)
+ m_ready = 1'b0;
+ if (o_axi_arvalid && !i_axi_arready)
+ m_ready = 1'b0;
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write transactions
+ //
+
+ // awvalid, wvalid
+ // {{{
+ // Send write transactions
+ initial o_axi_awvalid = 0;
+ initial o_axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ o_axi_awvalid <= 0;
+ o_axi_wvalid <= 0;
+ end else if (m_valid && m_we && m_ready)
+ begin
+ o_axi_awvalid <= 1;
+ o_axi_wvalid <= 1;
+ end else begin
+ if (i_axi_awready)
+ o_axi_awvalid <= 0;
+ if (i_axi_wready)
+ o_axi_wvalid <= 0;
+ end
+ // }}}
+
+ // wdata
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ o_axi_wdata <= {(C_AXI_DATA_WIDTH/DW){m_data}};
+ // }}}
+
+ // wstrb
+ // {{{
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin : NO_WSTRB_ADJUSTMENT
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ o_axi_wstrb <= m_sel;
+ // }}}
+ end else if (OPT_LITTLE_ENDIAN)
+ begin : LITTLE_ENDIAN_WSTRB
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ // Verilator lint_off WIDTH
+ o_axi_wstrb <= m_sel << ((DW/8) * m_addr[SUBW-1:0]);
+ // Verilator lint_on WIDTH
+ // }}}
+ end else begin : BIG_ENDIAN_WSTRB
+ // {{{
+ reg [SUBW-1:0] neg_addr;
+
+ always @(*)
+ neg_addr = ~m_addr[SUBW-1:0];
+
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ // Verilator lint_off WIDTH
+ o_axi_wstrb <= m_sel << ((DW/8)* neg_addr);
+ // Verilator lint_on WIDTH
+ // }}}
+ end endgenerate
+ // }}}
+
+ //
+ // Read transactions
+ //
+
+ // arvalid
+ // {{{
+ initial o_axi_arvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_axi_arvalid <= 0;
+ else if (m_valid && !m_we && m_ready)
+ o_axi_arvalid <= 1;
+ else if (i_axi_arready)
+ begin
+ o_axi_arvalid <= 0;
+ end
+ // }}}
+
+ // awaddr, araddr
+ // {{{
+ generate if (OPT_LITTLE_ENDIAN || DW == C_AXI_DATA_WIDTH)
+ begin : GEN_ADDR_LSBS
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_awvalid || i_axi_awready)
+ o_axi_awaddr <= { m_addr, {($clog2(DW)-3){1'b0}} };
+
+ always @(posedge i_clk)
+ if (!o_axi_arvalid || i_axi_arready)
+ o_axi_araddr <= { m_addr, {($clog2(DW)-3){1'b0}} };
+ // }}}
+ end else begin : OPT_BIG_ENDIAN
+ // {{{
+ reg [SUBW-1:0] neg_addr;
+
+ always @(*)
+ neg_addr = ~m_addr[SUBW-1:0];
+
+ always @(posedge i_clk)
+ if (!o_axi_awvalid || i_axi_awready)
+ begin
+ o_axi_awaddr <= 0;
+ o_axi_awaddr <= m_addr << ($clog2(DW)-3);
+ o_axi_awaddr[$clog2(DW)-3 +: SUBW] <= neg_addr;
+ end
+
+ always @(posedge i_clk)
+ if (!o_axi_arvalid || i_axi_arready)
+ begin
+ o_axi_araddr <= 0;
+ o_axi_araddr <= m_addr << ($clog2(DW)-3);
+ o_axi_araddr[$clog2(DW)-3 +: SUBW] <= neg_addr;
+ end
+ // }}}
+ end endgenerate
+ // }}}
+
+ // rdata, and returned o_wb_data, o_wb_ack, o_wb_err
+ // {{{
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin : NO_READ_DATA_SELECT_NECESSARY
+ // {{{
+ always @(*)
+ o_wb_data = i_axi_rdata;
+
+ always @(*)
+ o_wb_ack = !flushing&&((i_axi_rvalid && !i_axi_rresp[1])
+ ||(i_axi_bvalid && !i_axi_bresp[1]));
+
+ always @(*)
+ o_wb_err = !flushing&&((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1]));
+ // }}}
+ end else begin : READ_FIFO_DATA_SELECT
+ // {{{
+
+ reg [SUBW-1:0] addr_fifo [0:(1<<LGFIFO)-1];
+ reg [SUBW-1:0] fifo_value;
+ reg [LGFIFO:0] wr_addr, rd_addr;
+ wire [C_AXI_DATA_WIDTH-1:0] return_data;
+
+ initial o_wb_ack = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_wb_cyc || flushing)
+ o_wb_ack <= 0;
+ else
+ o_wb_ack <= ((i_axi_rvalid && !i_axi_rresp[1])
+ ||(i_axi_bvalid && !i_axi_bresp[1]));
+
+ initial o_wb_err = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_wb_cyc || flushing)
+ o_wb_err <= 0;
+ else
+ o_wb_err <= ((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1]));
+
+
+ initial wr_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ wr_addr <= 0;
+ else if (m_valid && m_ready)
+ wr_addr <= wr_addr + 1;
+
+ always @(posedge i_clk)
+ if (m_valid && m_ready)
+ addr_fifo[wr_addr[LGFIFO-1:0]] <= m_addr[SUBW-1:0];
+
+ initial rd_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ rd_addr <= 0;
+ else if (i_axi_bvalid || i_axi_rvalid)
+ rd_addr <= rd_addr + 1;
+
+ always @(*)
+ fifo_value = addr_fifo[rd_addr[LGFIFO-1:0]];
+
+ if (OPT_LITTLE_ENDIAN)
+ begin : LITTLE_ENDIAN_RDATA
+
+ assign return_data = i_axi_rdata >> (fifo_value * DW);
+
+ end else begin : BIG_ENDIAN_RDATA
+
+ reg [SUBW-1:0] neg_fifo_value;
+
+ always @(*)
+ neg_fifo_value = ~fifo_value;
+
+ assign return_data = i_axi_rdata
+ >> (neg_fifo_value * DW);
+
+ end
+
+ always @(posedge i_clk)
+ o_wb_data <= return_data[DW-1:0];
+
+ // Make Verilator happy here
+ // verilator lint_off UNUSED
+ if (C_AXI_DATA_WIDTH > DW)
+ begin : UNUSED_DATA
+ wire unused_data;
+ assign unused_data = &{ 1'b0,
+ return_data[C_AXI_DATA_WIDTH-1:DW] };
+ end
+ // verilator lint_on UNUSED
+`ifdef FORMAL
+ always @(*)
+ assert(wr_addr - rd_addr == npending);
+
+ always @(*)
+ assert(empty == (wr_addr == rd_addr));
+
+
+ //
+ // ...
+ //
+`endif
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Read data channel / response logic
+ assign o_axi_rready = 1'b1;
+ assign o_axi_bready = 1'b1;
+ // }}}
+
+ // Make verilator's -Wall happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, full, i_axi_bid, i_axi_bresp[0], i_axi_rid, i_axi_rresp[0], i_axi_rlast, m_data, m_sel };
+ generate if (C_AXI_DATA_WIDTH > DW)
+ begin : GEN_UNUSED_DW
+ wire [C_AXI_DATA_WIDTH-1:DW] unused_data;
+ assign unused_data = i_axi_rdata[C_AXI_DATA_WIDTH-1:DW];
+ end endgenerate
+ // verilator lint_on UNUSED
+ // }}}
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Formal methods section
+// {{{
+// Below are a scattering of the formal properties used. They are not the
+// complete set of properties. Those are maintained elsewhere.
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // ...
+ //
+
+ // Parameters
+ initial assert( (C_AXI_DATA_WIDTH / DW ==32)
+ ||(C_AXI_DATA_WIDTH / DW ==16)
+ ||(C_AXI_DATA_WIDTH / DW == 8)
+ ||(C_AXI_DATA_WIDTH / DW == 4)
+ ||(C_AXI_DATA_WIDTH / DW == 2)
+ ||(C_AXI_DATA_WIDTH == DW));
+ //
+ initial assert( C_AXI_ADDR_WIDTH == AW + (LG_WB_DW-3));
+
+
+ initial begin
+ assert(C_AXI_DATA_WIDTH >= DW);
+ assert((DW == 8) == (AW == C_AXI_ADDR_WIDTH));
+ assert(C_AXI_ADDR_WIDTH == AW + $clog2(DW)-3);
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Setup / f_past_valid
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions about the WISHBONE inputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ fwb_slave #(.DW(DW),.AW(AW),
+ .F_MAX_STALL(0),
+ .F_MAX_ACK_DELAY(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_REQUESTS(0))
+ f_wb(i_clk, i_reset, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr,
+ i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
+ f_wb_nreqs, f_wb_nacks, f_wb_outstanding);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions about the AXI inputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_axi(.i_clk(i_clk), .i_axi_reset_n(!i_reset),
+ // {{{
+ // Write address channel
+ .i_axi_awready(i_axi_awready),
+ .i_axi_awid( o_axi_awid),
+ .i_axi_awaddr( o_axi_awaddr),
+ .i_axi_awlen( o_axi_awlen),
+ .i_axi_awsize( o_axi_awsize),
+ .i_axi_awburst(o_axi_awburst),
+ .i_axi_awlock( o_axi_awlock),
+ .i_axi_awcache(o_axi_awcache),
+ .i_axi_awprot( o_axi_awprot),
+ .i_axi_awqos( o_axi_awqos),
+ .i_axi_awvalid(o_axi_awvalid),
+ // Write data channel
+ .i_axi_wready( i_axi_wready),
+ .i_axi_wdata( o_axi_wdata),
+ .i_axi_wstrb( o_axi_wstrb),
+ .i_axi_wlast( o_axi_wlast),
+ .i_axi_wvalid( o_axi_wvalid),
+ // Write response channel
+ .i_axi_bid( i_axi_bid),
+ .i_axi_bresp( i_axi_bresp),
+ .i_axi_bvalid( i_axi_bvalid),
+ .i_axi_bready( o_axi_bready),
+ // Read address channel
+ .i_axi_arready(i_axi_arready),
+ .i_axi_arid( o_axi_arid),
+ .i_axi_araddr( o_axi_araddr),
+ .i_axi_arlen( o_axi_arlen),
+ .i_axi_arsize( o_axi_arsize),
+ .i_axi_arburst(o_axi_arburst),
+ .i_axi_arlock( o_axi_arlock),
+ .i_axi_arcache(o_axi_arcache),
+ .i_axi_arprot( o_axi_arprot),
+ .i_axi_arqos( o_axi_arqos),
+ .i_axi_arvalid(o_axi_arvalid),
+ // Read data channel
+ .i_axi_rid( i_axi_rid),
+ .i_axi_rresp( i_axi_rresp),
+ .i_axi_rvalid( i_axi_rvalid),
+ .i_axi_rdata( i_axi_rdata),
+ .i_axi_rlast( i_axi_rlast),
+ .i_axi_rready( o_axi_rready),
+ // Counts
+ .f_axi_awr_nbursts(f_axi_awr_nbursts),
+ .f_axi_wr_pending(f_axi_wr_pending),
+ .f_axi_rd_nbursts(f_axi_rd_nbursts),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding)
+ //
+ // ...
+ //
+ // }}}
+ );
+
+ always @(*)
+ if (!flushing && i_wb_cyc)
+ assert(f_wb_outstanding == npending + (r_stb ? 1:0)
+ + ( ((C_AXI_DATA_WIDTH != DW)
+ && (o_wb_ack|o_wb_err))? 1:0));
+ else if (flushing && i_wb_cyc && !o_wb_err)
+ assert(f_wb_outstanding == (r_stb ? 1:0));
+
+ always @(*)
+ if (f_axi_awr_nbursts > 0)
+ begin
+ assert(direction);
+ assert(f_axi_rd_nbursts == 0);
+ assert(f_axi_awr_nbursts + (o_axi_awvalid ? 1:0) == npending);
+ assert(f_axi_wr_pending == (o_axi_wvalid&&!o_axi_awvalid ? 1:0));
+
+ //
+ // ...
+ //
+ end
+ always @(*)
+ if (o_axi_awvalid)
+ assert(o_axi_wvalid);
+
+ // Some quick read checks
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ begin
+ assert(!direction);
+ assert(f_axi_rd_nbursts+(o_axi_arvalid ? 1:0)
+ == npending);
+ assert(f_axi_awr_nbursts == 0);
+
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ if (direction)
+ begin
+ assert(npending == (o_axi_awvalid ? 1:0) + f_axi_awr_nbursts);
+ assert(!o_axi_arvalid);
+ assert(f_axi_rd_nbursts == 0);
+ assert(!i_axi_rvalid);
+ end else begin
+ assert(npending == (o_axi_arvalid ? 1:0) + f_axi_rd_nbursts);
+ assert(!o_axi_awvalid);
+ assert(!o_axi_wvalid);
+ assert(f_axi_awr_nbursts == 0);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Pending counter properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ begin
+ assert(npending <= { 1'b1, {(LGFIFO){1'b0}} });
+ assert(empty == (npending == 0));
+ assert(full == (npending == {1'b1, {(LGFIFO){1'b0}} }));
+ assert(nearfull == (npending >= {1'b0, {(LGFIFO){1'b1}} }));
+ if (full)
+ assert(!m_ready);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assertions about the AXI4 ouputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Write response channel
+ always @(posedge i_clk)
+ // We keep bready high, so the other condition doesn't
+ // need to be checked
+ assert(o_axi_bready);
+
+ // AXI read data channel signals
+ always @(posedge i_clk)
+ // We keep o_axi_rready high, so the other condition's
+ // don't need to be checked here
+ assert(o_axi_rready);
+
+ //
+ // AXI write address channel
+ //
+ //
+ always @(*)
+ begin
+ if (o_axi_awvalid || o_axi_wvalid || f_axi_awr_nbursts>0)
+ assert(direction);
+ //
+ // ...
+ //
+ end
+ //
+ // AXI read address channel
+ //
+ always @(*)
+ begin
+ if (o_axi_arvalid || i_axi_rvalid || f_axi_rd_nbursts > 0)
+ assert(!direction);
+ //
+ // ...
+ //
+ end
+
+ //
+ // AXI write response channel
+ //
+
+
+ //
+ // AXI read data channel signals
+ //
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Prove that a write to this address will change this value
+ //
+
+ // Some extra register declarations
+ // {{{
+ (* anyconst *) reg [C_AXI_ADDR_WIDTH-1:0] f_const_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] f_data;
+ // }}}
+
+ //
+ // Assume a basic bus response to the given data and address
+ //
+ integer iN;
+
+ // f_data
+ // {{{
+ initial f_data = 0;
+ always @(posedge i_clk)
+ if (o_axi_wvalid && i_axi_wready && o_axi_awaddr == f_const_addr)
+ begin
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (o_axi_wstrb[iN])
+ f_data[8*iN +: 8] <= o_axi_wdata[8*iN +: 8];
+ end
+ end
+ // }}}
+
+ // Assume RDATA == f_data if appropriate
+ // {{{
+ always @(*)
+ if (i_axi_rvalid && o_axi_rready && f_axi_rd_ckvalid
+ && (f_axi_rd_ckaddr == f_const_addr))
+ assume(i_axi_rdata == f_data);
+ // }}}
+
+ // f_wb_addr -- A WB address designed to match f_const_addr (AXI addr)
+ // {{{
+ always @(*)
+ begin
+ f_wb_addr = f_const_addr[C_AXI_ADDR_WIDTH-1:DWSIZE];
+ if (!OPT_LITTLE_ENDIAN && SUBW > 0)
+ f_wb_addr[0 +: SUBW] = ~f_wb_addr[0 +: SUBW];
+ end
+ // }}}
+
+ // Assume the address is Wishbone word aligned
+ // {{{
+ generate if (DW > 8)
+ begin
+ always @(*)
+ assume(f_const_addr[$clog2(DW)-4:0] == 0);
+ end endgenerate
+ // }}}
+
+ // f_axi_data -- Replicate f_wb_data across the whole word
+ // {{{
+ always @(*)
+ f_axi_data = {(C_AXI_DATA_WIDTH/DW){f_wb_data}};
+ // }}}
+
+ //
+ // ...
+ //
+
+ always @(*)
+ begin
+ f_valid_wb_response = 1;
+ for(iN=0; iN<DW/8; iN=iN+1)
+ begin
+ if (f_wb_strb[iN] && (o_wb_data[iN*8 +: 8] != f_wb_data[iN*8 +: 8]))
+ f_valid_wb_response = 0;
+ end
+ end
+ // }}}
+
+ // f_valid_axi_data
+ // {{{
+ always @(*)
+ begin
+ f_valid_axi_data = 1;
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (f_axi_strb[iN] && (f_axi_data[iN*8 +: 8] != f_data[iN*8 +: 8]))
+ f_valid_axi_data = 0;
+ end
+ end
+ // }}}
+
+ // f_valid_axi_response
+ // {{{
+ always @(*)
+ begin
+ f_valid_axi_response = 1;
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (f_axi_strb[iN] && (i_axi_rdata[iN*8 +: 8] != f_data[iN*8 +: 8]))
+ f_valid_axi_response = 0;
+ end
+ end
+ // }}}
+
+ //
+ // ...
+ //
+
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin
+
+ always @(*)
+ f_axi_strb = f_wb_strb;
+
+ end else if (OPT_LITTLE_ENDIAN)
+ begin
+
+ always @(*)
+ f_axi_strb <= f_wb_strb << ( (DW/8) *
+ f_wb_addr[SUBW-1:0]);
+
+ end else // if (!OPT_LITTLE_ENDIAN)
+ begin
+ reg [SUBW-1:0] f_neg_addr;
+
+ always @(*)
+ f_neg_addr = ~f_wb_addr[SUBW-1:0];
+
+ always @(*)
+ f_axi_strb <= f_wb_strb << ( (DW/8) * f_neg_addr );
+
+ end endgenerate
+ // }}}
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Ad-hoc assertions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (DW > 8)
+ begin
+ always @(*)
+ if (o_axi_awvalid)
+ assert(o_axi_awaddr[$clog2(DW)-4:0] == 0);
+
+ always @(*)
+ if (o_axi_arvalid)
+ assert(o_axi_araddr[$clog2(DW)-4:0] == 0);
+
+ end endgenerate
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [F_LGDEPTH-1:0] r_hit_reads, r_hit_writes,
+ r_check_fault, check_fault,
+ cvr_nreads, cvr_nwrites;
+ reg cvr_flushed, cvr_read2write, cvr_write2read;
+
+ initial r_hit_reads = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_hit_writes <= 0;
+ else if (f_axi_awr_nbursts > 3)
+ r_hit_writes <= 1;
+
+ initial r_hit_reads = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_hit_reads <= 0;
+ else if (f_axi_rd_nbursts > 3)
+ r_hit_reads <= 1;
+
+ always @(*)
+ begin
+ check_fault = 0;
+ if (!i_wb_cyc && o_axi_awvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && o_axi_wvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && f_axi_awr_nbursts > 0)
+ check_fault = 1;
+ if (!i_wb_cyc && i_axi_bvalid)
+ check_fault = 1;
+ //
+ if (!i_wb_cyc && o_axi_arvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && f_axi_rd_outstanding > 0)
+ check_fault = 1;
+ if (!i_wb_cyc && i_axi_rvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && (o_wb_ack | o_wb_err))
+ check_fault = 1;
+
+ if (flushing)
+ check_fault = 1;
+ end
+
+ initial r_check_fault = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_check_fault <= 0;
+ else if (check_fault)
+ r_check_fault <= 1;
+
+ always @(*)
+ cover(r_hit_writes && r_hit_reads && f_axi_rd_nbursts == 0
+ && f_axi_awr_nbursts == 0
+ && !o_axi_awvalid && !o_axi_arvalid && !o_axi_wvalid
+ && !i_axi_bvalid && !i_axi_rvalid
+ && !o_wb_ack && !o_wb_stall && !i_wb_stb
+ && !check_fault && !r_check_fault);
+
+ //
+ // ...
+ //
+
+ initial cvr_flushed = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_flushed <= 1'b0;
+ else if (flushing)
+ cvr_flushed <= 1'b1;
+
+ always @(*)
+ begin
+ cover(!i_reset && cvr_flushed && !flushing);
+ cover(!i_reset && cvr_flushed && !flushing && !o_wb_stall);
+ end
+
+ //
+ // Let's cover our ability to turn around, from reads to writes or from
+ // writes to reads.
+ //
+ // Note that without the RMW option above, switching direction requires
+ // dropping i_wb_cyc. Let's just make certain here, that if we do so,
+ // we don't drop it until after all of the returns come back.
+ //
+ initial cvr_read2write = 0;
+ always @(posedge i_clk)
+ if (i_reset || (!i_wb_cyc && f_wb_nreqs != f_wb_nacks))
+ cvr_read2write <= 0;
+ else if (!direction && !empty && m_we)
+ cvr_read2write <= 1;
+
+ initial cvr_write2read = 0;
+ always @(posedge i_clk)
+ if (i_reset || (!i_wb_cyc && f_wb_nreqs != f_wb_nacks))
+ cvr_write2read <= 0;
+ else if (direction && !empty && !m_we)
+ cvr_write2read <= 1;
+
+ always @(*)
+ begin
+ cover(cvr_read2write && direction && o_wb_ack && f_wb_outstanding == 1);
+ cover(cvr_write2read && !direction && o_wb_ack && f_wb_outstanding == 1);
+ end
+
+ reg [2:0] cvr_ack_after_abort;
+
+ initial cvr_ack_after_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_ack_after_abort <= 0;
+ else begin
+ if (!i_wb_cyc)
+ cvr_ack_after_abort[2:0] <= (empty) ? 0 : 3'b01;
+ if (cvr_ack_after_abort[0] && i_wb_cyc && r_stb && flushing)
+ cvr_ack_after_abort[1] <= 1;
+ if (o_wb_ack && &cvr_ack_after_abort[1:0])
+ cvr_ack_after_abort[2] <= 1;
+ end
+
+ always @(*)
+ cover(&cvr_ack_after_abort[1:0]);
+ always @(*)
+ cover(!flushing && (&cvr_ack_after_abort[1:0]));
+ always @(*)
+ cover(&cvr_ack_after_abort[2:0]);
+ always @(*)
+ cover(!i_wb_cyc && &cvr_ack_after_abort[2:0]);
+
+ initial cvr_nwrites = 0;
+ always @(posedge i_clk)
+ if (i_reset || flushing || !i_wb_cyc || !i_wb_we || o_wb_err)
+ cvr_nwrites <= 0;
+ else if (i_axi_bvalid && o_axi_bready)
+ cvr_nwrites <= cvr_nwrites + 1;
+
+ initial cvr_nreads = 0;
+ always @(posedge i_clk)
+ if (i_reset || flushing || !i_wb_cyc || i_wb_we || o_wb_err)
+ cvr_nreads <= 0;
+ else if (i_axi_rvalid && o_axi_rready)
+ cvr_nreads <= cvr_nreads + 1;
+
+ always @(*)
+ cover(cvr_nwrites == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc);
+
+ always @(*)
+ cover(cvr_nreads == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc);
+
+ //
+ // Generate a cover that doesn't include an abort
+ // {{{
+ (* anyconst *) reg f_never_abort;
+
+ always @(*)
+ if (f_never_abort && f_wb_nacks != f_wb_nreqs)
+ assume(!i_reset && i_wb_cyc && !o_wb_err);
+
+ always @(posedge i_clk)
+ if (f_never_abort && $past(o_wb_ack) && o_wb_ack)
+ assume($changed(o_wb_data));
+
+ always @(*)
+ cover(cvr_nreads == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc
+ && f_never_abort);
+ // }}}
+
+ // }}}
+`endif // FORMAL
+// }}}
+endmodule
diff --git a/rtl/wb2axip/wbp2classic.v b/rtl/wb2axip/wbp2classic.v
new file mode 100644
index 0000000..250489b
--- /dev/null
+++ b/rtl/wb2axip/wbp2classic.v
@@ -0,0 +1,205 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbp2classic.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Takes a WB pipelined connection from a master, and converts it
+// to WB classic for the slave.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbp2classic #(
+ // {{{
+ parameter AW = 12,
+ DW = 32
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ //
+ // Incoming WB pipelined port
+ input wire i_scyc, i_sstb, i_swe,
+ input wire [AW-1:0] i_saddr,
+ input wire [DW-1:0] i_sdata,
+ input wire [DW/8-1:0] i_ssel,
+ output reg o_sstall, o_sack,
+ output reg [DW-1:0] o_sdata,
+ output reg o_serr,
+ //
+ // Outgoing WB classic port
+ output reg o_mcyc, o_mstb, o_mwe,
+ output reg [AW-1:0] o_maddr,
+ output reg [DW-1:0] o_mdata,
+ output reg [DW/8-1:0] o_msel,
+ input wire i_mack,
+ input wire [DW-1:0] i_mdata,
+ input wire i_merr,
+ // Extra wires, not necessarily necessary for WB/B3
+ output reg [2:0] o_mcti,
+ output reg [1:0] o_mbte
+ // }}}
+ );
+
+ //
+ // returned = whether we've received our return value or not.
+ reg returned;
+
+ // Combinatorial values forwarded downstream
+ // {{{
+ always @(*)
+ begin
+ o_mcyc = i_scyc;
+ o_mstb = i_sstb && !returned;
+ o_mwe = i_swe;
+ o_maddr = i_saddr;
+ o_mdata = i_sdata;
+ o_msel = i_ssel;
+
+ // Cycle type indicator: Classic
+ o_mcti = 3'b000;
+ // Burst type indicator--ignored for single transaction cycle
+ // types, such as the classic type above
+ o_mbte = 2'b00; // Linear burst
+ end
+ // }}}
+
+ // returned
+ // {{{
+ initial returned = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ returned <= 0;
+ else if (!i_sstb || returned)
+ returned <= 0;
+ else if (i_mack || i_merr)
+ returned <= 1;
+ // }}}
+
+ // o_sstall
+ // {{{
+ always @(*)
+ o_sstall = !returned;
+ // }}}
+
+ // o_sack, o_serr
+ // {{{
+ initial o_sack = 0;
+ initial o_serr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ o_sack <= 0;
+ o_serr <= 0;
+ end else begin
+ o_sack <= (i_scyc) && i_mack;
+ o_serr <= (i_scyc) && i_merr;
+ end
+ // }}}
+
+ // o_mdata
+ // {{{
+ always @(posedge i_clk)
+ if (i_mack || i_merr)
+ o_sdata <= i_mdata;
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_LGDEPTH = 1;
+ reg f_past_valid;
+ reg f_ongoing;
+ reg [F_LGDEPTH-1:0] f_nreqs, f_nacks, f_outstanding;
+
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid = 1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Upstream Wishbone pipeline slave properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ fwb_slave #(.AW(AW), .DW(DW),
+ .F_MAX_STALL(4),
+ .F_MAX_ACK_DELAY(15),
+ .F_LGDEPTH(1)) incoming (i_clk, i_reset,
+ i_scyc, i_sstb, i_swe, i_saddr, i_sdata, i_ssel,
+ o_sack, o_sstall, o_sdata, o_serr,
+ f_nreqs, f_nacks, f_outstanding);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Downstream Wishbone classic master properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ fwbc_master #(.AW(AW), .DW(DW), .F_MAX_DELAY(3))
+ classic (i_clk, i_reset,
+ o_mcyc, o_mstb, o_mwe, o_maddr, o_mdata, o_msel, o_mcti, o_mbte,
+ i_mack, i_mdata, i_merr, 1'b0);
+
+ // }}}
+
+ //
+ // Disallow bus aborts
+ always @(posedge i_clk)
+ f_ongoing <= (!i_reset && i_sstb && !(o_sack | o_serr));
+
+ always @(*)
+ if (f_ongoing)
+ assume(i_sstb);
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/wbsafety.v b/rtl/wb2axip/wbsafety.v
new file mode 100644
index 0000000..2636f11
--- /dev/null
+++ b/rtl/wb2axip/wbsafety.v
@@ -0,0 +1,587 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbsafety.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A WB bus fault isolator. This core will isolate any downstream
+// WB slave faults from the upstream channel. It sits as a bump
+// in the wire between upstream and downstream channels, and so it will
+// consume two clocks--slowing down the slave, but potentially allowing
+// the developer to recover in case of a fault.
+//
+// This core is configured by a couple parameters, which are key to its
+// functionality.
+//
+// OPT_TIMEOUT Set this to a number to be roughly the longest time
+// period you expect the slave to stall the bus, or likewise
+// the longest time period you expect it to wait for a response.
+// If the slave takes longer for either task, a fault will be
+// detected and reported.
+//
+// OPT_SELF_RESET If set, this will send a reset signal to the downstream
+// core so that you can attempt to restart it without reloading
+// the FPGA. If set, the o_reset signal will be used to reset
+// the downstream core.
+//
+// A second key feature of this core is the outgoing fault detector,
+// o_fault. If this signal is ever raised, the slave has (somehow)
+// violated protocol. Such a violation may (or may not) return an
+// error upstream. For example, if the slave returns a response
+// following no requests from the master, then no error will be returned
+// up stream (doing so would be a protocol violation), but a fault will
+// be detected. Use this line to trigger any internal logic analyzers.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbsafety #(
+ // {{{
+ parameter AW = 28, DW = 32,
+ parameter OPT_TIMEOUT = 12,
+ parameter MAX_DEPTH = (OPT_TIMEOUT),
+ parameter [0:0] OPT_SELF_RESET = 1'b1,
+ parameter [0:0] F_OPT_FAULTLESS = 1'b1
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ //
+ // The incoming WB interface from the (trusted) master
+ // {{{
+ // This interface is guaranteed to follow the protocol.
+ input wire i_wb_cyc, i_wb_stb, i_wb_we,
+ input wire [AW-1:0] i_wb_addr,
+ input wire [DW-1:0] i_wb_data,
+ input wire [DW/8-1:0] i_wb_sel,
+ output reg o_wb_stall, o_wb_ack,
+ output reg [DW-1:0] o_wb_idata,
+ output reg o_wb_err,
+ // }}}
+ //
+ // The outgoing interface to the untrusted slave
+ // {{{
+ // This interface may or may not follow the WB protocol
+ output reg o_reset,
+ output reg o_wb_cyc, o_wb_stb, o_wb_we,
+ output reg [AW-1:0] o_wb_addr,
+ output reg [DW-1:0] o_wb_data,
+ output reg [DW/8-1:0] o_wb_sel,
+ input wire i_wb_stall, i_wb_ack,
+ input wire [DW-1:0] i_wb_idata,
+ input wire i_wb_err,
+ // }}}
+ //
+ // The fault signal, indicating the downstream slave was
+ // misbehaving
+ output reg o_fault
+ // }}}
+ );
+
+ // Declarations
+ // {{{
+ localparam LGTIMEOUT = $clog2(OPT_TIMEOUT+1);
+ localparam LGDEPTH = $clog2(MAX_DEPTH+1);
+ reg none_expected;
+ reg [LGDEPTH-1:0] expected_returns;
+ reg [LGTIMEOUT-1:0] stall_timer, wait_timer;
+ reg timeout;
+ reg faulty_return;
+
+ wire skd_stb, skd_o_ready, skd_we;
+ reg skd_stall;
+ wire [AW-1:0] skd_addr;
+ wire [DW-1:0] skd_data;
+ wire [DW/8-1:0] skd_sel;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Start with a skid buffer on all incoming signals
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`ifdef FORMAL
+ // {{{
+ // We know the skid buffer works. It's irrelevant to our proof.
+ // Therefore, remove it during formal testing, lest we need to
+ // check it as well. Further, we make this a parameter--but only
+ // when FORMAL is defined--so that it may be overridden in that case.
+ //
+ parameter [0:0] SKID_PASSTHROUGH = 1'b1;
+`else
+ localparam [0:0] SKID_PASSTHROUGH = 1'b0;
+ // }}}
+`endif
+
+ skidbuffer #(
+ // {{{
+ .DW(1+AW+DW+(DW/8)),
+ .OPT_PASSTHROUGH(SKID_PASSTHROUGH)
+ // }}}
+ ) skd(
+ // {{{
+ .i_clk(i_clk), .i_reset(i_reset || !i_wb_cyc),
+ .i_valid(i_wb_stb), .o_ready(skd_o_ready),
+ .i_data({ i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }),
+ .o_valid(skd_stb), .i_ready(!skd_stall),
+ .o_data({ skd_we, skd_addr, skd_data, skd_sel })
+ // }}}
+ );
+
+ always @(*)
+ o_wb_stall = !skd_o_ready;
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Timeout checking
+ //
+
+
+ //
+ // Insist on a maximum number of downstream stalls
+ //
+ initial stall_timer = 0;
+ always @(posedge i_clk)
+ if (!i_reset && o_wb_stb && i_wb_stall)
+ begin
+ if (stall_timer <= OPT_TIMEOUT)
+ stall_timer <= stall_timer + 1;
+ end else
+ stall_timer <= 0;
+
+ //
+ // Insist on a maximum number cyles waiting for an acknowledgment
+ //
+ initial wait_timer = 0;
+ always @(posedge i_clk)
+ if (!i_reset && o_wb_cyc && !o_wb_stb && !i_wb_ack && !i_wb_err
+ && !none_expected)
+ begin
+ if (wait_timer <= OPT_TIMEOUT)
+ wait_timer <= wait_timer + 1;
+ end else
+ wait_timer <= 0;
+
+ //
+ // Generate a timeout signal on any error
+ //
+ initial timeout = 0;
+ always @(posedge i_clk)
+ if (timeout && o_wb_err)
+ timeout <= 0;
+ else
+ timeout <= (i_wb_stall)&&(stall_timer >= OPT_TIMEOUT)
+ || ((!i_wb_ack && !i_wb_err)&&(wait_timer >= OPT_TIMEOUT));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Return counting
+ // {{{
+
+ initial none_expected = 1;
+ initial expected_returns = 0;
+ always @(posedge i_clk)
+ if (i_reset || o_reset || o_wb_err || !i_wb_cyc)
+ begin
+ expected_returns <= 0;
+ none_expected <= 1;
+ end else case({skd_stb && !skd_stall, o_wb_ack })
+ 2'b10: begin
+ expected_returns <= expected_returns + 1;
+ none_expected <= 1'b0;
+ end
+ 2'b01: begin
+ expected_returns <= expected_returns - 1;
+ none_expected <= (expected_returns == 1);
+ end
+ default: begin end
+ endcase
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Downstream reset generation
+ // {{{
+ generate if (OPT_SELF_RESET)
+ begin : SELF_RESET
+
+ initial o_reset = 1;
+ always @(posedge i_clk)
+ if (i_reset || o_fault)
+ o_reset <= 1;
+ else begin
+ o_reset <= 0;
+ if (o_wb_cyc && none_expected
+ &&(i_wb_ack || i_wb_err))
+ o_reset <= 1;
+ if (timeout)
+ o_reset <= 1;
+ end
+ end else begin : FORWARD_RESET
+
+ always @(*)
+ o_reset = i_reset || o_fault;
+
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Fault detection
+ // {{{
+
+ // faulty_return
+ // {{{
+ // A faulty return is a response from the slave at a time, or in
+ // a fashion that it unexpected and violates protocol
+ //
+ always @(*)
+ begin
+ faulty_return = 0;
+ if (expected_returns <= ((o_wb_stb && i_wb_stall) ? 1:0)
+ + ((o_wb_ack || o_wb_err) ? 1:0))
+ faulty_return = i_wb_ack || i_wb_err;
+ if (i_wb_ack && i_wb_err)
+ faulty_return = 1;
+ if (!i_wb_cyc || !o_wb_cyc)
+ faulty_return = 0;
+ end
+ // }}}
+
+ // o_fault
+ // {{{
+ initial o_fault = 0;
+ always @(posedge i_clk)
+ if (o_reset && !i_wb_cyc)
+ o_fault <= 0;
+ else begin
+ if (o_wb_cyc && faulty_return)
+ o_fault <= 1;
+ if (i_wb_cyc && timeout)
+ o_fault <= 1;
+ end
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Downstream bus signal generation
+ //
+
+ // o_wb_cyc
+ // {{{
+ initial o_wb_cyc = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset || (o_wb_cyc && i_wb_err) || o_reset || o_fault
+ || (i_wb_cyc && o_wb_err))
+ o_wb_cyc <= 1'b0;
+ else
+ o_wb_cyc <= i_wb_cyc && (o_wb_cyc || i_wb_stb);
+ // }}}
+
+ // o_wb_stb
+ // {{{
+ initial o_wb_stb = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset || (o_wb_cyc && i_wb_err) || o_reset || o_fault || !i_wb_cyc
+ || (i_wb_cyc && o_wb_err))
+ o_wb_stb <= 1'b0;
+ else if (!o_wb_stb || !i_wb_stall)
+ o_wb_stb <= skd_stb;
+ // }}}
+
+ // o_wb_we, o_wb_addr, o_wb_data, o_wb_sel
+ // {{{
+ always @(posedge i_clk)
+ if (!o_wb_stb || !i_wb_stall)
+ begin
+ o_wb_we <= skd_we;
+ o_wb_addr <= skd_addr;
+ o_wb_data <= skd_data;
+ o_wb_sel <= skd_sel;
+ end
+ // }}}
+
+ // o_wb_idata
+ // {{{
+ always @(posedge i_clk)
+ o_wb_idata <= i_wb_idata;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Return signal generation
+ //
+
+ // skd_stall
+ // {{{
+ always @(*)
+ begin
+ skd_stall = (o_wb_stb && i_wb_stall);
+ if (i_reset)
+ skd_stall = 1'b1;
+ if (o_fault)
+ skd_stall = 1'b0;
+ else if (o_reset)
+ skd_stall = 1'b1;
+ end
+ // }}}
+
+ // o_wb_ack, o_wb_err
+ // {{{
+ initial o_wb_ack = 0;
+ initial o_wb_err = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_wb_cyc)
+ begin
+ o_wb_ack <= 1'b0;
+ o_wb_err <= 1'b0;
+ end else if (!o_reset && !o_fault)
+ begin
+ if (timeout || faulty_return || i_wb_err)
+ begin
+ o_wb_ack <= 1'b0;
+ o_wb_err <= (expected_returns > ((o_wb_ack||o_wb_err) ? 1:0));
+ end else begin
+ o_wb_ack <= o_wb_cyc && i_wb_ack && !i_wb_err;
+ o_wb_err <= 1'b0;
+ end
+ end else begin
+ o_wb_ack <= 1'b0;
+ o_wb_err <= (i_wb_stb && !skd_stall);
+ end
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, F_OPT_FAULTLESS };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The following proof comes in several parts.
+ //
+ // 1. PROVE that the upstream properties will hold independent of
+ // what the downstream slave ever does.
+ //
+ // 2. PROVE that if the downstream slave follows protocol, then
+ // o_fault will never get raised.
+ //
+ // We then repeat these proofs again with both OPT_SELF_RESET set and
+ // clear. Which of the four proofs is accomplished is dependent upon
+ // parameters set by the formal engine.
+ //
+ //
+ localparam DOWNSTREAM_ACK_DELAY = OPT_TIMEOUT/2-1;
+ localparam UPSTREAM_ACK_DELAY = OPT_TIMEOUT + 3;
+ wire [LGDEPTH-1:0] fwbs_nreqs, fwbs_nacks, fwbs_outstanding;
+
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Upstream master Bus properties
+ //
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ fwb_slave #(
+ // {{{
+ .AW(AW), .DW(DW),
+ // .F_MAX_ACK_DELAY(UPSTREAM_ACK_DELAY),
+ .F_LGDEPTH(LGDEPTH),
+ .F_OPT_DISCONTINUOUS(1),
+ .F_OPT_MINCLOCK_DELAY(0)
+ // }}}
+ ) wbs (
+ // {{{
+ i_clk, i_reset,
+ i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_idata, o_wb_err,
+ fwbs_nreqs, fwbs_nacks, fwbs_outstanding
+ // }}}
+ );
+
+ always @(*)
+ assert(none_expected == (expected_returns == 0));
+
+ // Just so we pass the skid buffer's assumptions ...
+ always @(posedge i_clk)
+ if (f_past_valid && $past(i_wb_stb && o_wb_stall))
+ assume($stable(i_wb_data));
+
+ always @(*)
+ if (i_wb_cyc && !o_wb_err && !o_fault)
+ assert(expected_returns == fwbs_outstanding);
+
+ generate if (F_OPT_FAULTLESS)
+ begin : FAULTLESS_PROPERTIES
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ // Assume the downstream core is protocol compliant, and
+ // prove that o_fault stays low.
+ //
+ wire [LGDEPTH-1:0] fwbm_nreqs, fwbm_nacks,fwbm_outstanding;
+ reg [LGDEPTH-1:0] mreqs, sacks;
+
+
+ fwb_master #(
+ // {{{
+ .AW(AW), .DW(DW),
+ .F_MAX_ACK_DELAY(DOWNSTREAM_ACK_DELAY),
+ .F_MAX_STALL(DOWNSTREAM_ACK_DELAY),
+ .F_LGDEPTH(LGDEPTH),
+ .F_OPT_DISCONTINUOUS(1),
+ .F_OPT_MINCLOCK_DELAY(0)
+ // }}}
+ ) wbm (
+ // {{{
+ i_clk, o_reset,
+ o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
+ o_wb_sel,
+ i_wb_ack, i_wb_stall, i_wb_idata, i_wb_err,
+ fwbm_nreqs, fwbm_nacks, fwbm_outstanding
+ // }}}
+ );
+
+ //
+ // Here's the big proof
+ always @(*)
+ assert(!o_fault);
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // The following properties are necessary for passing induction
+ //
+ always @(*)
+ if (!i_reset && i_wb_cyc && o_wb_cyc)
+ assert(expected_returns == fwbm_outstanding
+ + (o_wb_stb ? 1:0)
+ + ((o_wb_ack|o_wb_err) ? 1:0));
+
+ always @(*)
+ assert(!timeout);
+
+ always @(*)
+ if (o_wb_err)
+ assert(!o_wb_cyc);
+
+ always @(*)
+ sacks = fwbs_nacks + (o_wb_ack ? 1:0);
+
+ always @(*)
+ if (!o_wb_err && i_wb_cyc && o_wb_cyc)
+ assert(sacks == fwbm_nacks);
+
+ always @(posedge i_clk)
+ if (!i_reset && i_wb_cyc && expected_returns > 0)
+ assert(o_wb_cyc || o_wb_err);
+
+ always @(*)
+ mreqs = fwbm_nreqs + (o_wb_stb ? 1:0);
+
+ always @(*)
+ if (!o_wb_err && i_wb_cyc && o_wb_cyc)
+ assert(fwbs_nreqs == mreqs);
+
+ always @(*)
+ if (i_wb_cyc && o_wb_cyc && fwbs_outstanding > 0)
+ assert(i_wb_we == o_wb_we);
+
+ always @(*)
+ if (fwbs_nacks != 0 && i_wb_cyc)
+ assert(o_wb_cyc || o_wb_err);
+ // }}}
+ end else begin
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ // cover() checks, checks that only make sense if faults are
+ // possible
+ //
+
+ always @(posedge i_clk)
+ cover(o_fault);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $past(faulty_return))
+ cover(o_fault);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $past(timeout))
+ cover(o_fault);
+
+ if (OPT_SELF_RESET)
+ begin
+ ////////////////////////////////////////////////////////
+ //
+ // Prove that we can actually reset the downstream
+ // bus/core as desired
+ //
+ reg faulted;
+
+ initial faulted = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ faulted <= 0;
+ else if (o_fault)
+ faulted <= 1;
+
+
+ always @(posedge i_clk)
+ cover(faulted && $fell(o_reset));
+
+ always @(posedge i_clk)
+ cover(faulted && !o_reset && o_wb_ack);
+
+ end
+ // }}}
+ end endgenerate
+
+ always @(*)
+ cover(!i_reset && fwbs_nacks > 4);
+
+`endif
+// }}}
+endmodule
diff --git a/rtl/wb2axip/wbxbar.v b/rtl/wb2axip/wbxbar.v
new file mode 100644
index 0000000..15d6587
--- /dev/null
+++ b/rtl/wb2axip/wbxbar.v
@@ -0,0 +1,1790 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbxbar.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A Configurable wishbone cross-bar interconnect, conforming
+// to the WB-B4 pipeline specification, as described on the
+// ZipCPU blog.
+//
+// Performance:
+// Throughput: One transaction per clock
+// Latency: One clock to get access to an unused channel, another to
+// place the results on the slave bus, and another to return, or a minimum
+// of three clocks.
+//
+// Usage: To use, you'll need to set NM and NS to the number of masters
+// (input ports) and the number of slaves respectively. You'll then
+// want to set the addresses for the slaves in the SLAVE_ADDR array,
+// together with the SLAVE_MASK array indicating which SLAVE_ADDRs
+// are valid. Address and data widths should be adjusted at the same
+// time.
+//
+// Voila, you are now set up!
+//
+// Now let's fine tune this:
+//
+// LGMAXBURST can be set to control the maximum number of outstanding
+// transactions. An LGMAXBURST of 6 will allow 63 outstanding
+// transactions.
+//
+// OPT_TIMEOUT, if set to a non-zero value, is a number of clock periods
+// to wait for a slave to respond. Should the timeout expire and the
+// slave not respond, a bus error will be returned and the slave will
+// be issued a bus abort signal (CYC will be dropped).
+//
+// OPT_STARVATION_TIMEOUT, if set, applies the OPT_TIMEOUT counter to
+// how long a particular master waits for arbitration. If the master is
+// "starved", a bus error will be returned.
+//
+// OPT_DBLBUFFER is used to increase clock speed by registering all
+// outputs.
+//
+// OPT_LOWPOWER is an experimental feature that, if set, will cause any
+// unused FFs to be set to zero rather than flopping in the electronic
+// wind, in an effort to minimize transitions over bus wires. This will
+// cost some extra logic, for ... an uncertain power savings.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+`default_nettype none
+// }}}
+module wbxbar #(
+ // {{{
+ parameter NM = 4, NS=8,
+ parameter AW = 32, DW=32,
+ parameter [NS*AW-1:0] SLAVE_ADDR = {
+ { 3'b111, {(AW-3){1'b0}} },
+ { 3'b110, {(AW-3){1'b0}} },
+ { 3'b101, {(AW-3){1'b0}} },
+ { 3'b100, {(AW-3){1'b0}} },
+ { 3'b011, {(AW-3){1'b0}} },
+ { 3'b010, {(AW-3){1'b0}} },
+ { 4'b0010, {(AW-4){1'b0}} },
+ { 4'b0000, {(AW-4){1'b0}} } },
+ parameter [NS*AW-1:0] SLAVE_MASK = (NS <= 1) ? 0
+ : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }},
+ {(2){ 4'b1111, {(AW-4){1'b0}} }} },
+ //
+ // LGMAXBURST is the log_2 of the length of the longest burst
+ // that might be seen. It's used to set the size of the
+ // internal counters that are used to make certain that the
+ // cross bar doesn't switch while still waiting on a response.
+ parameter LGMAXBURST=6,
+ //
+ // OPT_TIMEOUT is used to help recover from a misbehaving slave.
+ // If set, this value will determine the number of clock cycles
+ // to wait for a misbehaving slave before returning a bus error.
+ // Alternatively, if set to zero, this functionality will be
+ // removed.
+ parameter OPT_TIMEOUT = 0, // 1023;
+ //
+ // If OPT_TIMEOUT is set, then OPT_STARVATION_TIMEOUT may also
+ // be set. The starvation timeout adds to the bus error timeout
+ // generation the possibility that a master will wait
+ // OPT_TIMEOUT counts without receiving the bus. This may be
+ // the case, for example, if one bus master is consuming a
+ // peripheral to such an extent that there's no time/room for
+ // another bus master to use it. In that case, when the timeout
+ // runs out, the waiting bus master will be given a bus error.
+ parameter [0:0] OPT_STARVATION_TIMEOUT = 1'b0
+ && (OPT_TIMEOUT > 0),
+ //
+ // OPT_DBLBUFFER is used to register all of the outputs, and
+ // thus avoid adding additional combinational latency through
+ // the core that might require a slower clock speed.
+ parameter [0:0] OPT_DBLBUFFER = 1'b0,
+ //
+ // OPT_LOWPOWER adds logic to try to force unused values to
+ // zero, rather than to allow a variety of logic optimizations
+ // that could be used to reduce the logic count of the device.
+ // Hence, OPT_LOWPOWER will use more logic, but it won't drive
+ // bus wires unless there's a value to drive onto them.
+ parameter [0:0] OPT_LOWPOWER = 1'b1
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ //
+ // Here are the bus inputs from each of the WB bus masters
+ input wire [NM-1:0] i_mcyc, i_mstb, i_mwe,
+ input wire [NM*AW-1:0] i_maddr,
+ input wire [NM*DW-1:0] i_mdata,
+ input wire [NM*DW/8-1:0] i_msel,
+ //
+ // .... and their return data
+ output wire [NM-1:0] o_mstall,
+ output wire [NM-1:0] o_mack,
+ output reg [NM*DW-1:0] o_mdata,
+ output wire [NM-1:0] o_merr,
+ //
+ //
+ // Here are the output ports, used to control each of the
+ // various slave ports that we are connected to
+ output reg [NS-1:0] o_scyc, o_sstb, o_swe,
+ output reg [NS*AW-1:0] o_saddr,
+ output reg [NS*DW-1:0] o_sdata,
+ output reg [NS*DW/8-1:0] o_ssel,
+ //
+ // ... and their return data back to us.
+ input wire [NS-1:0] i_sstall, i_sack,
+ input wire [NS*DW-1:0] i_sdata,
+ input wire [NS-1:0] i_serr
+ // }}}
+ );
+ //
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register declarations
+ // {{{
+ //
+ // TIMEOUT_WIDTH is the number of bits in counter used to check
+ // on a timeout.
+ localparam TIMEOUT_WIDTH = $clog2(OPT_TIMEOUT);
+ //
+ // LGNM is the log (base two) of the number of bus masters
+ // connecting to this crossbar
+ localparam LGNM = (NM>1) ? $clog2(NM) : 1;
+ //
+ // LGNS is the log (base two) of the number of slaves plus one
+ // come out of the system. The extra "plus one" is used for a
+ // pseudo slave representing the case where the given address
+ // doesn't connect to any of the slaves. This address will
+ // generate a bus error.
+ localparam LGNS = $clog2(NS+1);
+ // At one time I used o_macc and o_sacc to put into the outgoing
+ // trace file, just enough logic to tell me if a transaction was
+ // taking place on the given clock.
+ //
+ // assign o_macc = (i_mstb & ~o_mstall);
+ // assign o_sacc = (o_sstb & ~i_sstall);
+ //
+ // These definitions work with Veri1ator, just not with Yosys
+ // reg [NM-1:0][NS:0] request;
+ // reg [NM-1:0][NS-1:0] requested;
+ // reg [NM-1:0][NS:0] grant;
+ //
+ // These definitions work with both
+ wire [NS:0] request [0:NM-1];
+ reg [NS-1:0] requested [0:NM-1];
+ reg [NS:0] grant [0:NM-1];
+ reg [NM-1:0] mgrant;
+ reg [NS-1:0] sgrant;
+
+ // Verilator lint_off UNUSED
+ wire [LGMAXBURST-1:0] w_mpending [0:NM-1];
+ // Verilator lint_on UNUSED
+ reg [NM-1:0] mfull, mnearfull, mempty;
+ wire [NM-1:0] timed_out;
+
+ localparam NMFULL = (NM > 1) ? (1<<LGNM) : 1;
+ localparam NSFULL = (1<<LGNS);
+
+ wire [LGNS-1:0] mindex [0:NMFULL-1];
+ wire [LGNM-1:0] sindex [0:NSFULL-1];
+
+ wire [NMFULL-1:0] m_cyc;
+ wire [NMFULL-1:0] m_stb;
+ wire [NMFULL-1:0] m_we;
+ wire [AW-1:0] m_addr [0:NMFULL-1];
+ wire [DW-1:0] m_data [0:NMFULL-1];
+ wire [DW/8-1:0] m_sel [0:NMFULL-1];
+ reg [NM-1:0] m_stall;
+ //
+ wire [NSFULL-1:0] s_stall;
+ wire [DW-1:0] s_data [0:NSFULL-1];
+ wire [NSFULL-1:0] s_ack;
+ wire [NSFULL-1:0] s_err;
+ wire [NM-1:0] dcd_stb;
+
+ localparam [0:0] OPT_BUFFER_DECODER=(NS != 1 || SLAVE_MASK != 0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming signal arbitration
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ genvar N, M;
+ integer iN, iM;
+ generate for(N=0; N<NM; N=N+1)
+ begin : DECODE_REQUEST
+ // {{{
+ // Register declarations
+ // {{{
+ wire skd_stb, skd_stall;
+ wire skd_we;
+ wire [AW-1:0] skd_addr;
+ wire [DW-1:0] skd_data;
+ wire [DW/8-1:0] skd_sel;
+ wire [NS:0] decoded;
+ wire iskd_ready;
+ // }}}
+
+ skidbuffer #(
+ // {{{
+ // Can't run OPT_LOWPOWER here, less we mess up the
+ // consistency in skd_we following
+ //
+ // .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(1+AW+DW+DW/8),
+`ifdef FORMAL
+ .OPT_PASSTHROUGH(1),
+`endif
+ .OPT_OUTREG(0)
+ // }}}
+ ) iskid (
+ // {{{
+ .i_clk(i_clk),
+ .i_reset(i_reset || !i_mcyc[N]),
+ .i_valid(i_mstb[N]), .o_ready(iskd_ready),
+ .i_data({ i_mwe[N], i_maddr[N*AW +: AW],
+ i_mdata[N*DW +: DW],
+ i_msel[N*DW/8 +: DW/8] }),
+ .o_valid(skd_stb), .i_ready(!skd_stall),
+ .o_data({ skd_we, skd_addr, skd_data, skd_sel })
+ // }}}
+ );
+
+ assign o_mstall[N] = !iskd_ready;
+
+ addrdecode #(
+ // {{{
+ // Can't run OPT_LOWPOWER here, less we mess up the
+ // consistency in m_we following
+ //
+ // .OPT_LOWPOWER(OPT_LOWPOWER),
+ .NS(NS), .AW(AW), .DW(DW+DW/8+1),
+ .SLAVE_ADDR(SLAVE_ADDR),
+ .SLAVE_MASK(SLAVE_MASK),
+ .OPT_REGISTERED(OPT_BUFFER_DECODER)
+ // }}}
+ ) adcd(
+ // {{{
+ .i_clk(i_clk), .i_reset(i_reset),
+ .i_valid(skd_stb && i_mcyc[N]), .o_stall(skd_stall),
+ .i_addr(skd_addr),
+ .i_data({ skd_we, skd_data, skd_sel }),
+ .o_valid(dcd_stb[N]), .i_stall(m_stall[N]&&i_mcyc[N]),
+ .o_decode(decoded), .o_addr(m_addr[N]),
+ .o_data({ m_we[N], m_data[N], m_sel[N] })
+ // }}}
+ );
+
+ assign request[N] = (m_cyc[N] && dcd_stb[N]) ? decoded : 0;
+
+ assign m_cyc[N] = i_mcyc[N];
+ assign m_stb[N] = i_mcyc[N] && dcd_stb[N] && !mfull[N];
+ // }}}
+ end for(N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_MASTER_SIGNALS
+ // {{{
+ // in case NM isn't one less than a power of two, complete
+ // the set
+ assign m_cyc[N] = 0;
+ assign m_stb[N] = 0;
+
+ assign m_we[N] = 0;
+ assign m_addr[N] = 0;
+ assign m_data[N] = 0;
+ assign m_sel[N] = 0;
+ // }}}
+ end endgenerate
+
+ // requested
+ // {{{
+ always @(*)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ // For each slave
+ requested[0][iM] = 0;
+ for(iN=1; iN<NM; iN=iN+1)
+ begin
+ // This slave has been requested if a prior
+ // master has requested it
+ //
+ // This includes any master before the last one
+ requested[iN][iM] = requested[iN-1][iM];
+ //
+ // As well as if the last master has requested
+ // this slave. Only count this request, though,
+ // if this master could act upon it.
+ if (request[iN-1][iM] &&
+ (grant[iN-1][iM]
+ || (!mgrant[iN-1]||mempty[iN-1])))
+ requested[iN][iM] = 1;
+ end
+ end
+ end
+ // }}}
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : SLAVE_GRANT
+ // {{{
+`define REGISTERED_SGRANT
+`ifdef REGISTERED_SGRANT
+ // {{{
+ reg drop_sgrant;
+
+ // drop_sgrant
+ // {{{
+ always @(*)
+ begin
+ drop_sgrant = !m_cyc[sindex[M]];
+ if (!request[sindex[M]][M] && m_stb[sindex[M]]
+ && mempty[sindex[M]])
+ drop_sgrant = 1;
+ if (!sgrant[M])
+ drop_sgrant = 0;
+ if (i_reset)
+ drop_sgrant = 1;
+ end
+ // }}}
+
+ // sgrant
+ // {{{
+ initial sgrant[M] = 0;
+ always @(posedge i_clk)
+ begin
+ sgrant[M] <= sgrant[M];
+ for(iN=0; iN<NM; iN=iN+1)
+ if (request[iN][M] && (!mgrant[iN] || mempty[iN]))
+ sgrant[M] <= 1;
+ if (drop_sgrant)
+ sgrant[M] <= 0;
+ end
+ // }}}
+ // }}}
+`else
+ // {{{
+ // sgrant
+ // {{{
+ always @(*)
+ begin
+ sgrant[M] = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (grant[iN][M])
+ sgrant[M] = 1;
+ end
+ // }}}
+ // }}}
+`endif
+
+ assign s_data[M] = i_sdata[M*DW +: DW];
+ assign s_stall[M] = o_sstb[M] && i_sstall[M];
+ assign s_ack[M] = o_scyc[M] && i_sack[M];
+ assign s_err[M] = o_scyc[M] && i_serr[M];
+
+ // }}}
+ end for(M=NS; M<NSFULL; M=M+1)
+ begin : UNUSED_SLAVE_SIGNALS
+ // {{{
+ assign s_data[M] = 0;
+ assign s_stall[M] = 1;
+ assign s_ack[M] = 0;
+ assign s_err[M] = 1;
+ // }}}
+ end endgenerate
+
+ //
+ // Arbitrate among masters to determine who gets to access a given
+ // channel
+ generate for(N=0; N<NM; N=N+1)
+ begin : ARBITRATE_REQUESTS
+ // {{{
+
+ // Register declarations
+ // {{{
+ wire [NS:0] regrant;
+ wire [LGNS-1:0] reindex;
+
+ // This is done using a couple of variables.
+ //
+ // request[N][M]
+ // This is true if master N is requesting to access slave
+ // M. It is combinatorial, so it will be true if the
+ // request is being made on the current clock.
+ //
+ // requested[N][M]
+ // True if some other master, prior to N, has requested
+ // channel M. This creates a basic priority arbiter,
+ // such that lower numbered masters have access before
+ // a greater numbered master
+ //
+ // grant[N][M]
+ // True if a grant has been made for master N to access
+ // slave channel M
+ //
+ // mgrant[N]
+ // True if master N has been granted access to some slave
+ // channel, any channel.
+ //
+ // mindex[N]
+ // This is the number of the slave channel that master
+ // N has been given access to
+ //
+ // sgrant[M]
+ // True if there exists some master, N, that has been
+ // granted access to this slave, hence grant[N][M] must
+ // also be true
+ //
+ // sindex[M]
+ // This is the index of the master that has access to
+ // slave M, assuming sgrant[M]. Hence, if sgrant[M]
+ // then grant[sindex[M]][M] must be true
+ //
+ reg stay_on_channel;
+ reg requested_channel_is_available;
+ // }}}
+
+ // stay_on_channel
+ // {{{
+ always @(*)
+ begin
+ stay_on_channel = |(request[N] & grant[N]);
+
+ if (mgrant[N] && !mempty[N])
+ stay_on_channel = 1;
+ end
+ // }}}
+
+ // requested_channel_is_available
+ // {{{
+ always @(*)
+ begin
+ requested_channel_is_available =
+ |(request[N][NS-1:0]& ~sgrant & ~requested[N][NS-1:0]);
+
+ if (request[N][NS])
+ requested_channel_is_available = 1;
+
+ if (NM < 2)
+ requested_channel_is_available = m_stb[N];
+ end
+ // }}}
+
+ // grant, mgrant
+ // {{{
+ initial grant[N] = 0;
+ initial mgrant[N] = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_mcyc[N])
+ begin
+ grant[N] <= 0;
+ mgrant[N] <= 0;
+ end else if (!stay_on_channel)
+ begin
+ if (requested_channel_is_available)
+ begin
+ mgrant[N] <= 1'b1;
+ grant[N] <= request[N];
+ end else if (m_stb[N])
+ begin
+ mgrant[N] <= 1'b0;
+ grant[N] <= 0;
+ end
+ end
+ // }}}
+
+ if (NS == 1)
+ begin : MINDEX_ONE_SLAVE
+ // {{{
+ assign mindex[N] = 0;
+ assign regrant = 0;
+ assign reindex = 0;
+ // }}}
+ end else begin : MINDEX_MULTIPLE_SLAVES
+ // {{{
+ reg [LGNS-1:0] r_mindex;
+
+`define NEW_MINDEX_CODE
+`ifdef NEW_MINDEX_CODE
+ // {{{
+ reg [NS:0] r_regrant;
+ reg [LGNS-1:0] r_reindex;
+
+ // r_regrant
+ // {{{
+ always @(*)
+ begin
+ r_regrant = 0;
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ if (grant[N][iM])
+ // Maintain any open channels
+ r_regrant[iM] = 1'b1;
+ else if (!sgrant[iM]&&!requested[N][iM])
+ r_regrant[iM] = 1'b1;
+
+ if (!request[N][iM])
+ r_regrant[iM] = 1'b0;
+ end
+
+ if (grant[N][NS])
+ r_regrant[NS] = 1;
+ if (!request[N][NS])
+ r_regrant[NS] = 0;
+
+ if (mgrant[N] && !mempty[N])
+ r_regrant = 0;
+ end
+ // }}}
+
+ // r_reindex
+ // {{{
+ // Verilator lint_off BLKSEQ
+ always @(r_regrant, regrant)
+ begin
+ r_reindex = 0;
+ for(iM=0; iM<=NS; iM=iM+1)
+ if (r_regrant[iM])
+ r_reindex = r_reindex | iM[LGNS-1:0];
+ if (regrant == 0)
+ r_reindex = r_mindex;
+ end
+ // Verilator lint_on BLKSEQ
+ // }}}
+
+ always @(posedge i_clk)
+ r_mindex <= reindex;
+
+ assign reindex = r_reindex;
+ assign regrant = r_regrant;
+ // }}}
+`else
+ // {{{
+ always @(posedge i_clk)
+ if (!mgrant[N] || mempty[N])
+ begin
+
+ for(iM=0; iM<NS; iM=iM+1)
+ begin
+ if (request[N][iM] && grant[N][iM])
+ begin
+ // Maintain any open channels
+ r_mindex <= iM;
+ end else if (request[N][iM]
+ && !sgrant[iM]
+ && !requested[N][iM])
+ begin
+ // Open a new channel
+ // if necessary
+ r_mindex <= iM;
+ end
+ end
+ end
+
+ // }}}
+`endif // NEW_MINDEX_CODE
+ assign mindex[N] = r_mindex;
+ // }}}
+ end
+ // }}}
+ end for (N=NM; N<NMFULL; N=N+1)
+ begin : UNUSED_MINDEXES
+ // {{{
+ assign mindex[N] = 0;
+ // }}}
+ end endgenerate
+
+ // Calculate sindex. sindex[M] (indexed by slave ID)
+ // references the master controlling this slave. This makes for
+ // faster/cheaper logic on the return path, since we can now use
+ // a fully populated LUT rather than a priority based return scheme
+ generate for(M=0; M<NS; M=M+1)
+ begin : GEN_SINDEX
+ // {{{
+ if (NM <= 1)
+ begin : SINDEX_SINGLE_MASTER
+ // {{{
+ // If there will only ever be one master, then we
+ // can assume all slave indexes point to that master
+ assign sindex[M] = 0;
+ // }}}
+ end else begin : SINDEX_MORE_THAN_ONE_MASTER
+ // {{{
+ reg [LGNM-1:0] r_sindex;
+`define NEW_SINDEX_CODE
+`ifdef NEW_SINDEX_CODE
+ // {{{
+ reg [NM-1:0] regrant;
+ reg [LGNM-1:0] reindex;
+
+ always @(*)
+ begin
+ regrant = 0;
+ for (iN=0; iN<NM; iN=iN+1)
+ begin
+ // Each bit depends upon 6 inputs, so
+ // one 6-LUT should be sufficient
+ if (grant[iN][M])
+ regrant[iN] = 1;
+ else if (!sgrant[M]&& !requested[iN][M])
+ regrant[iN] = 1;
+
+ if (!request[iN][M])
+ regrant[iN] = 0;
+ if (mgrant[iN] && !mempty[iN])
+ regrant[iN] = 0;
+ end
+ end
+
+ always @(*)
+ begin
+ reindex = 0;
+ // Each bit in reindex depends upon all of the
+ // bits in regrant--should still be one LUT
+ // per bit though
+ if (regrant == 0)
+ reindex = sindex[M];
+ else for(iN=0; iN<NM; iN=iN+1)
+ if (regrant[iN])
+ reindex = reindex | iN[LGNM-1:0];
+ end
+
+ always @(posedge i_clk)
+ r_sindex <= reindex;
+
+ assign sindex[M] = r_sindex;
+ // }}}
+`else
+ // {{{
+ always @(posedge i_clk)
+ for (iN=0; iN<NM; iN=iN+1)
+ begin
+ if (!mgrant[iN] || mempty[iN])
+ begin
+ if (request[iN][M] && grant[iN][M])
+ r_sindex <= iN;
+ else if (request[iN][M] && !sgrant[M]
+ && !requested[iN][M])
+ r_sindex <= iN;
+ end
+ end
+
+ assign sindex[M] = r_sindex;
+ // }}}
+`endif
+ // }}}
+ end
+ // }}}
+ end for(M=NS; M<NSFULL; M=M+1)
+ begin : UNUSED_SINDEXES
+ // {{{
+ // Assign the unused slave indexes to zero
+ //
+ // Remember, to full out a full 2^something set of slaves,
+ // we may have more slave indexes than we actually have slaves
+
+ assign sindex[M] = 0;
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assign outputs to the slaves
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Part one
+ //
+ // In this part, we assign the difficult outputs: o_scyc and o_sstb
+ generate for(M=0; M<NS; M=M+1)
+ begin : GEN_CYC_STB
+ // {{{
+ initial o_scyc[M] = 0;
+ initial o_sstb[M] = 0;
+ always @(posedge i_clk)
+ begin
+ if (sgrant[M])
+ begin
+
+ if (!i_mcyc[sindex[M]])
+ begin
+ o_scyc[M] <= 1'b0;
+ o_sstb[M] <= 1'b0;
+ end else begin
+ o_scyc[M] <= 1'b1;
+
+ if (!o_sstb[M] || !s_stall[M])
+ o_sstb[M]<=request[sindex[M]][M]
+ && !mfull[sindex[M]];
+ end
+ end else begin
+ o_scyc[M] <= 1'b0;
+ o_sstb[M] <= 1'b0;
+ end
+
+ if (i_reset || s_err[M])
+ begin
+ o_scyc[M] <= 1'b0;
+ o_sstb[M] <= 1'b0;
+ end
+ end
+ // }}}
+ end endgenerate
+
+ //
+ // Part two
+ //
+ // These are the easy(er) outputs, since there are fewer properties
+ // riding on them
+ generate if ((NM == 1) && (!OPT_LOWPOWER))
+ begin : ONE_MASTER
+ // {{{
+ reg r_swe;
+ reg [AW-1:0] r_saddr;
+ reg [DW-1:0] r_sdata;
+ reg [DW/8-1:0] r_ssel;
+
+ //
+ // This is the low logic version of our bus data outputs.
+ // It only works if we only have one master.
+ //
+ // The basic idea here is that we share all of our bus outputs
+ // between all of the various slaves. Since we only have one
+ // bus master, this works.
+ //
+ always @(posedge i_clk)
+ begin
+ r_swe <= o_swe[0];
+ r_saddr <= o_saddr[0+:AW];
+ r_sdata <= o_sdata[0+:DW];
+ r_ssel <=o_ssel[0+:DW/8];
+
+ // Verilator lint_off WIDTH
+ if (sgrant[mindex[0]] && !s_stall[mindex[0]])
+ // Verilator lint_on WIDTH
+ begin
+ r_swe <= m_we[0];
+ r_saddr <= m_addr[0];
+ r_sdata <= m_data[0];
+ r_ssel <= m_sel[0];
+ end
+ end
+
+ //
+ // The original version set o_s*[0] above, and then
+ // combinatorially the rest of o_s* here below. That broke
+ // Veri1ator. Hence, we're using r_s* and setting all of o_s*
+ // here.
+ for(M=0; M<NS; M=M+1)
+ begin : FOREACH_SLAVE_PORT
+ always @(*)
+ begin
+ o_swe[M] = r_swe;
+ o_saddr[M*AW +: AW] = r_saddr[AW-1:0];
+ o_sdata[M*DW +: DW] = r_sdata[DW-1:0];
+ o_ssel[M*DW/8+:DW/8]= r_ssel[DW/8-1:0];
+ end
+ end
+ // }}}
+ end else begin : J
+ for(M=0; M<NS; M=M+1)
+ begin : GEN_DOWNSTREAM
+ // {{{
+ always @(posedge i_clk)
+ begin
+ if (OPT_LOWPOWER && !sgrant[M])
+ begin
+ o_swe[M] <= 1'b0;
+ o_saddr[M*AW +: AW] <= 0;
+ o_sdata[M*DW +: DW] <= 0;
+ o_ssel[M*(DW/8)+:DW/8]<= 0;
+ end else if (!s_stall[M]) begin
+ o_swe[M] <= m_we[sindex[M]];
+ o_saddr[M*AW +: AW] <= m_addr[sindex[M]];
+ if (OPT_LOWPOWER && !m_we[sindex[M]])
+ o_sdata[M*DW +: DW] <= 0;
+ else
+ o_sdata[M*DW +: DW] <= m_data[sindex[M]];
+ o_ssel[M*(DW/8)+:DW/8]<= m_sel[sindex[M]];
+ end
+
+ end
+ // }}}
+ end end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assign return values to the masters
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_DBLBUFFER)
+ begin : DOUBLE_BUFFERRED_STALL
+ // {{{
+ reg [NM-1:0] r_mack, r_merr;
+
+ for(N=0; N<NM; N=N+1)
+ begin : FOREACH_MASTER_PORT
+ // m_stall isn't buffered, since it depends upon
+ // the already existing buffer within the address
+ // decoder
+ always @(*)
+ begin
+ if (grant[N][NS])
+ m_stall[N] = 1;
+ else if (mgrant[N] && request[N][mindex[N]])
+ m_stall[N] = mfull[N] || s_stall[mindex[N]];
+ else
+ m_stall[N] = m_stb[N];
+
+ if (o_merr[N])
+ m_stall[N] = 0;
+ end
+
+ initial r_mack[N] = 0;
+ initial r_merr[N] = 0;
+ always @(posedge i_clk)
+ begin
+ // Verilator lint_off WIDTH
+ iM = mindex[N];
+ // Verilator lint_on WIDTH
+ r_mack[N] <= mgrant[N] && s_ack[mindex[N]];
+ r_merr[N] <= mgrant[N] && s_err[mindex[N]];
+ if (OPT_LOWPOWER && !mgrant[N])
+ o_mdata[N*DW +: DW] <= 0;
+ else
+ o_mdata[N*DW +: DW] <= s_data[mindex[N]];
+
+ if (grant[N][NS]||(timed_out[N] && !o_mack[N]))
+ begin
+ r_mack[N] <= 1'b0;
+ r_merr[N] <= !o_merr[N];
+ end
+
+ if (i_reset || !i_mcyc[N] || o_merr[N])
+ begin
+ r_mack[N] <= 1'b0;
+ r_merr[N] <= 1'b0;
+ end
+ end
+
+ assign o_mack[N] = r_mack[N];
+
+ assign o_merr[N] = (!OPT_STARVATION_TIMEOUT || i_mcyc[N]) && r_merr[N];
+
+ end
+ // }}}
+ end else if (NS == 1) // && !OPT_DBLBUFFER
+ begin : SINGLE_SLAVE
+ // {{{
+ for(N=0; N<NM; N=N+1)
+ begin : FOREACH_MASTER_PORT
+ reg r_mack, r_merr;
+
+ always @(*)
+ begin
+ m_stall[N] = !mgrant[N] || s_stall[0]
+ || (m_stb[N] && !request[N][0]);
+ r_mack = mgrant[N] && i_sack[0];
+ r_merr = mgrant[N] && i_serr[0];
+ o_mdata[N*DW +: DW] = (!mgrant[N] && OPT_LOWPOWER)
+ ? 0 : i_sdata;
+
+ if (mfull[N])
+ m_stall[N] = 1'b1;
+
+ if (timed_out[N] && !r_mack)
+ begin
+ m_stall[N] = 1'b0;
+ r_mack = 1'b0;
+ r_merr = 1'b1;
+ end
+
+ if (grant[N][NS] && m_stb[N])
+ begin
+ m_stall[N] = 1'b0;
+ r_mack = 1'b0;
+ r_merr = 1'b1;
+ end
+
+ if (!m_cyc[N])
+ begin
+ r_mack = 1'b0;
+ r_merr = 1'b0;
+ end
+ end
+
+ assign o_mack[N] = r_mack;
+ assign o_merr[N] = r_merr;
+ end
+ // }}}
+ end else begin : SINGLE_BUFFER_STALL
+ // {{{
+ for(N=0; N<NM; N=N+1)
+ begin : FOREACH_MASTER_PORT
+ // initial o_mstall[N] = 0;
+ // initial o_mack[N] = 0;
+ reg r_mack, r_merr;
+
+ always @(*)
+ begin
+ m_stall[N] = 1;
+ r_mack = mgrant[N] && s_ack[mindex[N]];
+ r_merr = mgrant[N] && s_err[mindex[N]];
+ if (OPT_LOWPOWER && !mgrant[N])
+ o_mdata[N*DW +: DW] = 0;
+ else
+ o_mdata[N*DW +: DW] = s_data[mindex[N]];
+
+ if (mgrant[N])
+ // Possibly lower the stall signal
+ m_stall[N] = s_stall[mindex[N]]
+ || !request[N][mindex[N]];
+
+ if (mfull[N])
+ m_stall[N] = 1'b1;
+
+ if (grant[N][NS] ||(timed_out[N] && !r_mack))
+ begin
+ m_stall[N] = 1'b0;
+ r_mack = 1'b0;
+ r_merr = 1'b1;
+ end
+
+ if (!m_cyc[N])
+ begin
+ r_mack = 1'b0;
+ r_merr = 1'b0;
+ end
+ end
+
+ assign o_mack[N] = r_mack;
+ assign o_merr[N] = r_merr;
+ end
+ // }}}
+ end endgenerate
+
+ //
+ // Count the pending transactions per master
+ generate for(N=0; N<NM; N=N+1)
+ begin : COUNT_PENDING_TRANSACTIONS
+ // {{{
+ reg [LGMAXBURST-1:0] lclpending;
+ initial lclpending = 0;
+ initial mempty[N] = 1;
+ initial mnearfull[N] = 0;
+ initial mfull[N] = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_mcyc[N] || o_merr[N])
+ begin
+ lclpending <= 0;
+ mfull[N] <= 0;
+ mempty[N] <= 1'b1;
+ mnearfull[N]<= 0;
+ end else case({ (m_stb[N] && !m_stall[N]), o_mack[N] })
+ 2'b01: begin
+ lclpending <= lclpending - 1'b1;
+ mnearfull[N]<= mfull[N];
+ mfull[N] <= 1'b0;
+ mempty[N] <= (lclpending == 1);
+ end
+ 2'b10: begin
+ lclpending <= lclpending + 1'b1;
+ mnearfull[N]<= (&lclpending[LGMAXBURST-1:2])&&(lclpending[1:0] != 0);
+ mfull[N] <= mnearfull[N];
+ mempty[N] <= 1'b0;
+ end
+ default: begin end
+ endcase
+
+ assign w_mpending[N] = lclpending;
+ // }}}
+ end endgenerate
+
+ generate if (OPT_TIMEOUT > 0)
+ begin : CHECK_TIMEOUT
+ // {{{
+ for(N=0; N<NM; N=N+1)
+ begin : FOREACH_MASTER_PORT
+
+ reg [TIMEOUT_WIDTH-1:0] deadlock_timer;
+ reg r_timed_out;
+
+ initial deadlock_timer = OPT_TIMEOUT;
+ initial r_timed_out = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_mcyc[N]
+ ||((w_mpending[N] == 0) && !m_stb[N])
+ ||(m_stb[N] && !m_stall[N])
+ ||(o_mack[N] || o_merr[N])
+ ||(!OPT_STARVATION_TIMEOUT&&!mgrant[N]))
+ begin
+ deadlock_timer <= OPT_TIMEOUT;
+ r_timed_out <= 0;
+ end else if (deadlock_timer > 0)
+ begin
+ deadlock_timer <= deadlock_timer - 1;
+ r_timed_out <= (deadlock_timer <= 1);
+ end
+
+ assign timed_out[N] = r_timed_out;
+ end
+ // }}}
+ end else begin : NO_TIMEOUT
+ // {{{
+ assign timed_out = 0;
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Parameter consistency check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial begin : PARAMETER_CONSISTENCY_CHECK
+ // {{{
+ if (NM == 0)
+ begin
+ $display("ERROR: At least one master must be defined");
+ $stop;
+ end
+
+ if (NS == 0)
+ begin
+ $display("ERROR: At least one slave must be defined");
+ $stop;
+ end
+
+ if (OPT_STARVATION_TIMEOUT != 0 && OPT_TIMEOUT == 0)
+ begin
+ $display("ERROR: The starvation timeout is implemented as part of the regular timeout");
+ $display(" Without a timeout, the starvation timeout will not work");
+ $stop;
+ end
+ // }}}
+ end
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties used to verify the core
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Register declarations
+ // {{{
+ localparam F_MAX_DELAY = 4;
+ localparam F_LGDEPTH = LGMAXBURST;
+ //
+ reg f_past_valid;
+ //
+ // Our bus checker keeps track of the number of requests,
+ // acknowledgments, and the number of outstanding transactions on
+ // every channel, both the masters driving us
+ wire [F_LGDEPTH-1:0] f_mreqs [0:NM-1];
+ wire [F_LGDEPTH-1:0] f_macks [0:NM-1];
+ wire [F_LGDEPTH-1:0] f_moutstanding [0:NM-1];
+ //
+ // as well as the slaves that we drive ourselves
+ wire [F_LGDEPTH-1:0] f_sreqs [0:NS-1];
+ wire [F_LGDEPTH-1:0] f_sacks [0:NS-1];
+ wire [F_LGDEPTH-1:0] f_soutstanding [0:NS-1];
+ // }}}
+
+ initial assert(!OPT_STARVATION_TIMEOUT || OPT_TIMEOUT > 0);
+
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid = 1'b1;
+
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ generate for(N=0; N<NM; N=N+1)
+ begin : GRANT_CHECKING
+ // {{{
+ reg checkgrant;
+
+ always @(*)
+ if (f_past_valid)
+ for(iN=N+1; iN<NM; iN=iN+1)
+ // Can't grant the same channel to two separate
+ // masters. This applies to all but the error or
+ // no-slave-selected channel
+ assert((grant[N][NS-1:0] & grant[iN][NS-1:0])==0);
+
+ for(M=1; M<=NS; M=M+1)
+ begin
+ // Can't grant two channels to the same master
+ always @(*)
+ if (f_past_valid && grant[N][M])
+ assert(grant[N][M-1:0] == 0);
+ end
+
+
+ always @(*)
+ if (&w_mpending[N])
+ assert(o_merr[N] || m_stall[N]);
+
+ always @(*)
+ if (f_past_valid)
+ begin
+ checkgrant = 0;
+ for(iM=0; iM<NS; iM=iM+1)
+ if (grant[N][iM])
+ checkgrant = 1;
+ if (grant[N][NS])
+ checkgrant = 1;
+
+ assert(checkgrant == mgrant[N]);
+ end
+ // }}}
+ end endgenerate
+
+ // Double check the grant mechanism and its dependent variables
+ generate for(N=0; N<NM; N=N+1)
+ begin : CHECK_GRANTS
+ // {{{
+ for(M=0; M<NS; M=M+1)
+ begin
+ always @(*)
+ if ((f_past_valid)&&grant[N][M])
+ begin
+ assert(mgrant[N]);
+ assert(mindex[N] == M);
+ assert(sgrant[M]);
+ assert(sindex[M] == N);
+ end
+ end
+ // }}}
+ end endgenerate
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : CHECK_SGRANT
+ // {{{
+ reg f_sgrant;
+
+ always @(*)
+ if (sgrant[M])
+ assert(grant[sindex[M]][M]);
+
+ always @(*)
+ begin
+ f_sgrant = 0;
+ for(iN=0; iN<NM; iN=iN+1)
+ if (grant[iN][M])
+ f_sgrant = 1;
+ end
+
+ always @(*)
+ assert(sgrant[M] == f_sgrant);
+ // }}}
+ end endgenerate
+
+ // Double check the timeout flags for consistency
+ generate for(N=0; N<NM; N=N+1)
+ begin : F_CHECK_TIMEOUT
+ // {{{
+ always @(*)
+ if (f_past_valid)
+ begin
+ assert(mempty[N] == (w_mpending[N] == 0));
+ assert(mnearfull[N]==(&w_mpending[N][LGMAXBURST-1:1]));
+ assert(mfull[N] == (&w_mpending[N]));
+ end
+ // }}}
+ end endgenerate
+
+`ifdef VERIFIC
+ // {{{
+ // The Verific parser is currently broken, and doesn't allow
+ // initial assumes or asserts. The following lines get us around that
+ //
+ always @(*)
+ if (!f_past_valid)
+ assume(sgrant == 0);
+
+ generate for(M=0; M<NS; M=M+1)
+ begin
+ always @(*)
+ if (!f_past_valid)
+ begin
+ assume(o_scyc[M] == 0);
+ assume(o_sstb[M] == 0);
+ assume(sgrant[M] == 0);
+ end
+ end endgenerate
+
+ generate for(N=0; N<NM; N=N+1)
+ begin
+ always @(*)
+ if (!f_past_valid)
+ begin
+ assume(grant[N] == 0);
+ assume(mgrant[N] == 0);
+ end
+ end endgenerate
+ // }}}
+`endif
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // BUS CHECK
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // Verify that every channel, whether master or slave, follows the rules
+ // of the WB road.
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : WB_SLAVE_CHECK
+ // {{{
+ fwb_slave #(
+ .AW(AW), .DW(DW),
+ .F_LGDEPTH(LGMAXBURST),
+ .F_MAX_ACK_DELAY(0),
+ .F_MAX_STALL(0)
+ ) slvi(i_clk, i_reset,
+ i_mcyc[N], i_mstb[N], i_mwe[N],
+ i_maddr[N*AW +: AW], i_mdata[N*DW +: DW],
+ i_msel[N*(DW/8) +: (DW/8)],
+ o_mack[N], o_mstall[N], o_mdata[N*DW +: DW], o_merr[N],
+ f_mreqs[N], f_macks[N], f_moutstanding[N]);
+
+ always @(*)
+ if ((f_past_valid)&&(grant[N][NS]))
+ assert(f_moutstanding[N] <= 1);
+
+ always @(*)
+ if (f_past_valid && grant[N][NS] && i_mcyc[N])
+ assert(m_stall[N] || o_merr[N]);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $past(!i_reset && i_mstb[N] && o_mstall[N]))
+ assume($stable(i_mdata[N*DW +: DW]));
+ // }}}
+ end endgenerate
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : WB_MASTER_CHECK
+ // {{{
+ fwb_master #(
+ .AW(AW), .DW(DW),
+ .F_LGDEPTH(LGMAXBURST),
+ .F_MAX_ACK_DELAY(F_MAX_DELAY),
+ .F_MAX_STALL(2)
+ ) mstri(i_clk, i_reset,
+ o_scyc[M], o_sstb[M], o_swe[M],
+ o_saddr[M*AW +: AW], o_sdata[M*DW +: DW],
+ o_ssel[M*(DW/8) +: (DW/8)],
+ i_sack[M], i_sstall[M], s_data[M], i_serr[M],
+ f_sreqs[M], f_sacks[M], f_soutstanding[M]);
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Correlate outstanding numbers
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate for(N=0; N<NM; N=N+1)
+ begin : CHECK_OUTSTANDING
+ // {{{
+ always @(*)
+ if (mfull[N])
+ assert(m_stall[N]);
+
+ always @(posedge i_clk)
+ if (i_mcyc[N])
+ assert(f_moutstanding[N] == w_mpending[N]
+ +((OPT_BUFFER_DECODER & dcd_stb[N]) ? 1:0));
+
+ reg [LGMAXBURST:0] n_outstanding;
+ always @(*)
+ if (i_mcyc[N])
+ assert(f_moutstanding[N] >=
+ ((OPT_BUFFER_DECODER && dcd_stb[N]) ? 1:0)
+ + (o_mack[N] && OPT_DBLBUFFER) ? 1:0);
+
+ always @(*)
+ n_outstanding = f_moutstanding[N]
+ - ((OPT_BUFFER_DECODER && dcd_stb[N]) ? 1:0)
+ - ((o_mack[N] && OPT_DBLBUFFER) ? 1:0);
+
+ always @(posedge i_clk)
+ if (i_mcyc[N] && !mgrant[N] && !o_merr[N])
+ assert(f_moutstanding[N]
+ == ((OPT_BUFFER_DECODER & dcd_stb[N]) ? 1:0));
+
+ else if (i_mcyc[N] && mgrant[N] && !i_reset)
+ for(iM=0; iM<NS; iM=iM+1)
+ if (grant[N][iM] && o_scyc[iM] && !i_serr[iM] && !o_merr[N])
+ assert(n_outstanding
+ == {1'b0,f_soutstanding[iM]}
+ +(o_sstb[iM] ? 1:0));
+
+ always @(*)
+ if (!i_reset)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ if (grant[N][iM] && i_mcyc[N])
+ begin
+ if (f_soutstanding[iM] > 0)
+ assert(i_mwe[N] == o_swe[iM]);
+ if (o_sstb[iM])
+ assert(i_mwe[N] == o_swe[iM]);
+ if (o_mack[N])
+ assert(i_mwe[N] == o_swe[iM]);
+ if (o_scyc[iM] && i_sack[iM])
+ assert(i_mwe[N] == o_swe[iM]);
+ if (o_merr[N] && !timed_out[N])
+ assert(i_mwe[N] == o_swe[iM]);
+ if (o_scyc[iM] && i_serr[iM])
+ assert(i_mwe[N] == o_swe[iM]);
+ end
+ end
+
+ always @(*)
+ if (!i_reset && OPT_BUFFER_DECODER && i_mcyc[N])
+ begin
+ if (dcd_stb[N])
+ assert(i_mwe[N] == m_we[N]);
+ end
+ // }}}
+ end endgenerate
+
+ generate for(M=0; M<NS; M=M+1)
+ begin : ASSERT_NOT_CYC_WO_GRANT
+ // {{{
+ always @(posedge i_clk)
+ if (!$past(sgrant[M]))
+ assert(!o_scyc[M]);
+ // }}}
+ end endgenerate
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // CONTRACT SECTION
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // Here's the contract, in two parts:
+ // {{{
+ // 1. Should ever a master (any master) wish to read from a slave
+ // (any slave), he should be able to read a known value
+ // from that slave (any value) from any arbitrary address
+ // he might wish to read from (any address)
+ //
+ // 2. Should ever a master (any master) wish to write to a slave
+ // (any slave), he should be able to write the exact
+ // value he wants (any value) to the exact address he wants
+ // (any address)
+ //
+ // special_master is an arbitrary constant chosen by the solver,
+ // which can reference *any* possible master
+ // special_address is an arbitrary constant chosen by the solver,
+ // which can reference *any* possible address the master
+ // might wish to access
+ // special_value is an arbitrary value (at least during
+ // induction) representing the current value within the
+ // slave at the given address
+ // }}}
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Now let's pay attention to a special bus master and a special
+ // address referencing a special bus slave. We'd like to assert
+ // that we can access the values of every slave from every master.
+ (* anyconst *) reg [(NM<=1)?0:(LGNM-1):0] special_master;
+ reg [(NS<=1)?0:(LGNS-1):0] special_slave;
+ (* anyconst *) reg [AW-1:0] special_address;
+ reg [DW-1:0] special_value;
+
+ always @(*)
+ if (NM <= 1)
+ assume(special_master == 0);
+ always @(*)
+ if (NS <= 1)
+ assume(special_slave == 0);
+
+ //
+ // Decode the special address to discover the slave associated with it
+ always @(*)
+ begin
+ special_slave = NS;
+ for(iM=0; iM<NS; iM = iM+1)
+ begin
+ if (((special_address ^ SLAVE_ADDR[iM*AW +: AW])
+ &SLAVE_MASK[iM*AW +: AW]) == 0)
+ special_slave = iM;
+ end
+ end
+
+ generate if (NS > 1)
+ begin : DOUBLE_ADDRESS_CHECK
+ // {{{
+ //
+ // Check that no slave address has been assigned twice.
+ // This check only needs to be done once at the beginning
+ // of the run, during the BMC section.
+ reg address_found;
+
+ always @(*)
+ if (!f_past_valid)
+ begin
+ address_found = 0;
+ for(iM=0; iM<NS; iM = iM+1)
+ begin
+ if (((special_address ^ SLAVE_ADDR[iM*AW +: AW])
+ &SLAVE_MASK[iM*AW +: AW]) == 0)
+ begin
+ assert(address_found == 0);
+ address_found = 1;
+ end
+ end
+ end
+ // }}}
+ end endgenerate
+ //
+ // Let's assume this slave will acknowledge any request on the next
+ // bus cycle after the stall goes low. Further, lets assume that
+ // it never creates an error, and that it always responds to our special
+ // address with the special data value given above. To do this, we'll
+ // also need to make certain that the special value will change
+ // following any write.
+ //
+ // These are the "assumptions" associated with our fictitious slave.
+`ifdef VERIFIC
+ always @(*)
+ if (!f_past_valid)
+ assume(special_value == 0);
+`else
+ initial assume(special_value == 0);
+`endif
+ always @(posedge i_clk)
+ if (special_slave < NS)
+ begin
+ // {{{
+ if ($past(o_sstb[special_slave] && !i_sstall[special_slave]))
+ begin
+ assume(i_sack[special_slave]);
+
+ if ($past(!o_swe[special_slave])
+ &&($past(o_saddr[special_slave*AW +: AW]) == special_address))
+ assume(i_sdata[special_slave*DW+: DW]
+ == special_value);
+ end else
+ assume(!i_sack[special_slave]);
+ assume(!i_serr[special_slave]);
+
+ if (o_scyc[special_slave])
+ assert(f_soutstanding[special_slave]
+ == i_sack[special_slave]);
+
+ if (o_sstb[special_slave] && !i_sstall[special_slave]
+ && o_swe[special_slave])
+ begin
+ for(iM=0; iM < DW/8; iM=iM+1)
+ if (o_ssel[special_slave * DW/8 + iM])
+ special_value[iM*8 +: 8] <= o_sdata[special_slave * DW + iM*8 +: 8];
+ end
+ // }}}
+ end
+
+ //
+ // Now its time to make some assertions. Specifically, we want to
+ // assert that any time we read from this special slave, the special
+ // value is returned.
+ reg [2:0] f_read_seq;
+ reg f_read_ack, f_read_sstall;
+
+ initial f_read_sstall = 0;
+ always @(posedge i_clk)
+ f_read_sstall <= s_stall[special_slave];
+
+ always @(*)
+ f_read_ack = (f_read_seq[2] || ((!OPT_DBLBUFFER)&&f_read_seq[1]
+ && !f_read_sstall));
+ initial f_read_seq = 0;
+ always @(posedge i_clk)
+ if ((special_master < NM)&&(special_slave < NS)
+ &&(i_mcyc[special_master])
+ &&(!timed_out[special_master]))
+ begin
+ f_read_seq <= 0;
+ if ((grant[special_master][special_slave])
+ &&(m_stb[special_master])
+ &&(m_addr[special_master] == special_address)
+ &&(!m_we[special_master])
+ )
+ begin
+ f_read_seq[0] <= 1;
+ end
+
+ if (|f_read_seq)
+ begin
+ assert(grant[special_master][special_slave]);
+ assert(mgrant[special_master]);
+ assert(sgrant[special_slave]);
+ assert(mindex[special_master] == special_slave);
+ assert(sindex[special_slave] == special_master);
+ assert(!o_merr[special_master]);
+ end
+
+ if (f_read_seq[0] && !$past(s_stall[special_slave]))
+ begin
+ assert(o_scyc[special_slave]);
+ assert(o_sstb[special_slave]);
+ assert(!o_swe[special_slave]);
+ assert(o_saddr[special_slave*AW +: AW] == special_address);
+
+ f_read_seq[1] <= 1;
+
+ end else if (f_read_seq[0] && $past(s_stall[special_slave]))
+ begin
+ assert($stable(m_stb[special_master]));
+ assert(!m_we[special_master]);
+ assert(m_addr[special_master] == special_address);
+
+ f_read_seq[0] <= 1;
+ end
+
+ if (f_read_seq[1] && $past(s_stall[special_slave]))
+ begin
+ assert(o_scyc[special_slave]);
+ assert(o_sstb[special_slave]);
+ assert(!o_swe[special_slave]);
+ assert(o_saddr[special_slave*AW +: AW] == special_address);
+ f_read_seq[1] <= 1;
+ end else if (f_read_seq[1] && !$past(s_stall[special_slave]))
+ begin
+ assert(i_sack[special_slave]);
+ assert(i_sdata[special_slave*DW +: DW] == $past(special_value));
+ if (OPT_DBLBUFFER)
+ f_read_seq[2] <= 1;
+ end
+
+ if (f_read_ack)
+ begin
+ assert(o_mack[special_master]);
+ assert(o_mdata[special_master * DW +: DW]
+ == $past(special_value,2));
+ end
+ end else
+ f_read_seq <= 0;
+
+ always @(*)
+ cover(i_mcyc[special_master] && f_read_ack);
+
+ //
+ // Let's try a write assertion now. Specifically, on any request to
+ // write to our special address, we want to assert that the special
+ // value at that address can be written.
+ reg [2:0] f_write_seq;
+ reg f_write_ack, f_write_sstall;
+
+ initial f_write_sstall = 0;
+ always @(posedge i_clk)
+ f_write_sstall = s_stall[special_slave];
+
+ always @(*)
+ f_write_ack = (f_write_seq[2]
+ || ((!OPT_DBLBUFFER)&&f_write_seq[1]
+ && !f_write_sstall));
+ initial f_write_seq = 0;
+ always @(posedge i_clk)
+ if ((special_master < NM)&&(special_slave < NS)
+ &&(i_mcyc[special_master])
+ &&(!timed_out[special_master]))
+ begin
+ f_write_seq <= 0;
+ if ((grant[special_master][special_slave])
+ &&(m_stb[special_master])
+ &&(m_addr[special_master] == special_address)
+ &&(m_we[special_master]))
+ begin
+ // Our write sequence begins when our special master
+ // has access to the bus, *and* he is trying to write
+ // to our special address.
+ f_write_seq[0] <= 1;
+ end
+
+ if (|f_write_seq)
+ begin
+ assert(grant[special_master][special_slave]);
+ assert(mgrant[special_master]);
+ assert(sgrant[special_slave]);
+ assert(mindex[special_master] == special_slave);
+ assert(sindex[special_slave] == special_master);
+ assert(!o_merr[special_master]);
+ end
+
+ if (f_write_seq[0] && !$past(s_stall[special_slave]))
+ begin
+ assert(o_scyc[special_slave]);
+ assert(o_sstb[special_slave]);
+ assert(o_swe[special_slave]);
+ assert(o_saddr[special_slave*AW +: AW] == special_address);
+ assert(o_sdata[special_slave*DW +: DW]
+ == $past(m_data[special_master]));
+ assert(o_ssel[special_slave*DW/8 +: DW/8]
+ == $past(m_sel[special_master]));
+
+ f_write_seq[1] <= 1;
+
+ end else if (f_write_seq[0] && $past(s_stall[special_slave]))
+ begin
+ assert($stable(m_stb[special_master]));
+ assert(m_we[special_master]);
+ assert(m_addr[special_master] == special_address);
+ assert($stable(m_data[special_master]));
+ assert($stable(m_sel[special_master]));
+
+ f_write_seq[0] <= 1;
+ end
+
+ if (f_write_seq[1] && $past(s_stall[special_slave]))
+ begin
+ assert(o_scyc[special_slave]);
+ assert(o_sstb[special_slave]);
+ assert(o_swe[special_slave]);
+ assert(o_saddr[special_slave*AW +: AW] == special_address);
+ assert($stable(o_sdata[special_slave*DW +: DW]));
+ assert($stable(o_ssel[special_slave*DW/8 +: DW/8]));
+ f_write_seq[1] <= 1;
+ end else if (f_write_seq[1] && !$past(s_stall[special_slave]))
+ begin
+ for(iM=0; iM<DW/8; iM=iM+1)
+ begin
+ if ($past(o_ssel[special_slave * DW/8 + iM]))
+ assert(special_value[iM*8 +: 8]
+ == $past(o_sdata[special_slave*DW+iM*8 +: 8]));
+ end
+
+ assert(i_sack[special_slave]);
+ if (OPT_DBLBUFFER)
+ f_write_seq[2] <= 1;
+ end
+
+ if (f_write_seq[2] || ((!OPT_DBLBUFFER) && f_write_seq[1]
+ && !$past(s_stall[special_slave])))
+ assert(o_mack[special_master]);
+ end else
+ f_write_seq <= 0;
+
+ always @(*)
+ cover(i_mcyc[special_master] && f_write_ack);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // COVER: Full connectivity check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [NM-1:0] f_m_ackd;
+ reg [NS-1:0] f_s_ackd;
+ reg f_cvr_aborted;
+
+ initial f_cvr_aborted = 0;
+ always @(posedge i_clk)
+ if (!f_past_valid || i_reset)
+ f_cvr_aborted <= 0;
+ else for(iN=0; iN<NM; iN=iN+1)
+ begin
+ if (request[iN][NS])
+ f_cvr_aborted = 1;
+ if ($fell(i_mcyc[iN]))
+ begin
+ if (f_macks[iN] != f_mreqs[iN])
+ f_cvr_aborted = 1;
+ end
+ end
+
+ initial f_m_ackd = 0;
+ generate for (N=0; N<NM; N=N+1)
+ begin : GEN_FM_ACKD
+
+ always @(posedge i_clk)
+ if (i_reset)
+ f_m_ackd[N] <= 0;
+ else if (o_mack[N])
+ f_m_ackd[N] <= 1'b1;
+
+ end endgenerate
+
+ always @(posedge i_clk)
+ cover(!f_cvr_aborted && (&f_m_ackd));
+
+ generate if (NM > 1)
+ begin
+
+ always @(posedge i_clk)
+ cover(!f_cvr_aborted && (&f_m_ackd[1:0]));
+
+ end endgenerate
+
+ initial f_s_ackd = 0;
+ generate for (M=0; M<NS; M=M+1)
+ begin : GEN_FS_ACKD
+
+ always @(posedge i_clk)
+ if (i_reset)
+ f_s_ackd[M] <= 1'b0;
+ else if (sgrant[M] && o_mack[sindex[M]])
+ f_s_ackd[M] <= 1'b1;
+
+ end endgenerate
+
+ always @(posedge i_clk)
+ cover(!f_cvr_aborted && (&f_s_ackd[NS-1:0]));
+
+ generate if (NS > 1)
+ begin
+
+ always @(posedge i_clk)
+ cover(!f_cvr_aborted && (&f_s_ackd[NS-1:0]));
+
+ end endgenerate
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif
diff --git a/rtl/wb2axip/wbxclk.v b/rtl/wb2axip/wbxclk.v
new file mode 100644
index 0000000..5f404b0
--- /dev/null
+++ b/rtl/wb2axip/wbxclk.v
@@ -0,0 +1,854 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: rtl/wbxclk.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: To cross clock domains with a (pipelined) wishbone bus.
+//
+// Challenges:
+// 1. Wishbone has no capacity for back pressure. That means that we'll
+// need to be careful not to issue more STB requests than ACKs
+// that will fit in the return buffer.
+//
+// Imagine, for example, a faster return clock but a slave that needs
+// many clocks to get going. During that time, many requests
+// might be issued. If they all suddenly get returned at once,
+// flooding the return ACK FIFO, then we have a problem.
+//
+// 2. Bus aborts. If we ever have to abort a transaction, that's going
+// to be a pain. The FIFOs will need to be reset and the
+// downstream CYC line dropped. This needs to be done
+// synchronously in both domains, but there's no real choice but
+// to make the crossing asynchronous.
+//
+// 3. Synchronous CYC. Lowering CYC is a normal part of the protocol, as
+// is raising CYC. CYC is used as a bus locking scheme, so we'll
+// need to know when it is (properly) lowered downstream. This
+// can be done by passing a synchronous CYC drop request through
+// the pipeline in addition to the bus aborts above.
+//
+// Status:
+// Formally verified against a set of bus properties, not yet
+// used in any real or simulated designs
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2020-2024, Gisselquist Technology, LLC
+// {{{
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP project contains free software and gateware, licensed under the
+// Apache License, Version 2.0 (the "License"). You may not use this project,
+// or this file, except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+`default_nettype none
+// }}}
+module wbxclk #(
+ // {{{
+ parameter AW=32,
+ DW=32,
+ LGFIFO = 5
+ // }}}
+ ) (
+ // {{{
+ input wire i_wb_clk, i_reset,
+ // Input/master bus
+ input wire i_wb_cyc, i_wb_stb, i_wb_we,
+ input wire [(AW-1):0] i_wb_addr,
+ input wire [(DW-1):0] i_wb_data,
+ input wire [(DW/8-1):0] i_wb_sel,
+ output wire o_wb_stall,
+ output reg o_wb_ack,
+ output reg [(DW-1):0] o_wb_data,
+ output reg o_wb_err,
+ // Delayed bus
+ input wire i_xclk_clk,
+ output reg o_xclk_cyc,
+ output reg o_xclk_stb,
+ output reg o_xclk_we,
+ output reg [(AW-1):0] o_xclk_addr,
+ output reg [(DW-1):0] o_xclk_data,
+ output reg [(DW/8-1):0] o_xclk_sel,
+ input wire i_xclk_stall,
+ input wire i_xclk_ack,
+ input wire [(DW-1):0] i_xclk_data,
+ input wire i_xclk_err
+ // }}}
+ );
+
+ //
+ // Declare our signals
+ // {{{
+ localparam NFF = 2;
+ reg wb_active;
+ reg [NFF-2:0] bus_abort_pipe;
+ reg [LGFIFO:0] acks_outstanding;
+ reg ackfifo_single, ackfifo_empty, ackfifo_full;
+ wire ack_stb, err_stb;
+ wire [DW-1:0] ret_wb_data;
+ wire req_fifo_stall, no_returns;
+ //
+ // Verilator lint_off SYNCASYNCNET
+ reg bus_abort;
+ // Verilator lint_on SYNCASYNCNET
+ //
+ wire req_stb, req_fifo_empty;
+ reg xclk_err_state, ign_ackfifo_stall;
+ reg xck_reset;
+ reg [NFF-2:0] xck_reset_pipe;
+ wire req_we;
+ wire [AW-1:0] req_addr;
+ wire [DW-1:0] req_data;
+ wire [DW/8-1:0] req_sel;
+`ifdef FORMAL
+ wire [LGFIFO:0] ackfifo_prefill, reqfifo_prefill;
+`endif
+ // }}}
+
+ //
+ //
+ // On the original wishbone clock ...
+ //
+ // FIFO/queue up our requests
+ // {{{
+ initial wb_active = 1'b0;
+ always @(posedge i_wb_clk)
+ if (i_reset || !i_wb_cyc)
+ wb_active <= 1'b0;
+ else if (i_wb_stb && !o_wb_stall)
+ wb_active <= 1'b1;
+
+ initial { bus_abort, bus_abort_pipe } = -1;
+ always @(posedge i_wb_clk)
+ if (i_reset)
+ { bus_abort, bus_abort_pipe } <= -1;
+ else if (!i_wb_cyc && (!ackfifo_empty))
+ { bus_abort, bus_abort_pipe } <= -1;
+ else if (o_wb_err)
+ { bus_abort, bus_abort_pipe } <= -1;
+ else if (ackfifo_empty)
+ { bus_abort, bus_abort_pipe } <= { bus_abort_pipe, 1'b0 };
+`ifdef FORMAL
+ always @(*)
+ if (bus_abort_pipe)
+ assert(bus_abort);
+`endif
+ // }}}
+
+ //
+ // The request FIFO itself
+ // {{{
+ afifo #(
+`ifdef FORMAL
+ .OPT_REGISTER_READS(0),
+ .F_OPT_DATA_STB(1'b0),
+`endif
+ .NFF(NFF), .LGFIFO(LGFIFO),
+ .WIDTH(2+AW+DW+(DW/8))
+ ) reqfifo(.i_wclk(i_wb_clk), .i_wr_reset_n(!bus_abort),
+ .i_wr((i_wb_stb&&!o_wb_stall) || (wb_active && !i_wb_cyc)),
+ .i_wr_data({ i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }),
+ .o_wr_full(req_fifo_stall),
+ //
+ .i_rclk(i_xclk_clk), .i_rd_reset_n(!xck_reset),
+ .i_rd(!o_xclk_stb || !i_xclk_stall),
+ .o_rd_data({ req_stb, req_we, req_addr, req_data, req_sel }),
+ .o_rd_empty(req_fifo_empty)
+`ifdef FORMAL
+ , .f_fill(reqfifo_prefill)
+`endif
+ );
+ // }}}
+
+ //
+ // Downstream bus--issuing requests
+ // {{{
+ initial { xck_reset, xck_reset_pipe } = -1;
+ always @(posedge i_xclk_clk or posedge bus_abort)
+ if (bus_abort)
+ { xck_reset, xck_reset_pipe } <= -1;
+ else
+ { xck_reset, xck_reset_pipe } <= { xck_reset_pipe, 1'b0 };
+`ifdef FORMAL
+ always @(*)
+ if (xck_reset_pipe)
+ assert(xck_reset);
+`endif
+
+ initial xclk_err_state = 1'b0;
+ always @(posedge i_xclk_clk)
+ if (xck_reset || (!req_fifo_empty && !req_stb))
+ xclk_err_state <= 1'b0;
+ else if (o_xclk_cyc && i_xclk_err)
+ xclk_err_state <= 1'b1;
+
+ initial o_xclk_cyc = 1'b0;
+ always @(posedge i_xclk_clk)
+ if (xck_reset || (o_xclk_cyc && i_xclk_err))
+ o_xclk_cyc <= 1'b0;
+ else if (!req_fifo_empty && !req_stb)
+ o_xclk_cyc <= 1'b0;
+ else if (!req_fifo_empty && !xclk_err_state)
+ o_xclk_cyc <= req_stb;
+
+ initial o_xclk_stb = 1'b0;
+ always @(posedge i_xclk_clk)
+ if (xck_reset || (o_xclk_cyc && i_xclk_err) || xclk_err_state)
+ o_xclk_stb <= 1'b0;
+ else if (!o_xclk_stb || !i_xclk_stall)
+ o_xclk_stb <= req_stb && !req_fifo_empty;
+
+ always @(posedge i_xclk_clk)
+ if ((!o_xclk_stb || !i_xclk_stall) && req_stb && !req_fifo_empty)
+ o_xclk_we <= req_we;
+
+ always @(posedge i_xclk_clk)
+ if (!o_xclk_stb || !i_xclk_stall)
+ begin
+ o_xclk_addr <= req_addr;
+ o_xclk_data <= req_data;
+ o_xclk_sel <= req_sel;
+ end
+ // }}}
+
+
+ //
+ // Request counting
+ // {{{
+ initial acks_outstanding = 0;
+ initial ackfifo_single = 0;
+ initial ackfifo_empty = 1;
+ initial ackfifo_full = 0;
+ always @(posedge i_wb_clk)
+ if (i_reset || !i_wb_cyc || o_wb_err)
+ begin
+ acks_outstanding <= 0;
+ ackfifo_single <= 0;
+ ackfifo_empty <= 1;
+ ackfifo_full <= 0;
+ end else case({ (i_wb_stb && !o_wb_stall), o_wb_ack })
+ 2'b10: begin
+ acks_outstanding <= acks_outstanding + 1;
+ ackfifo_empty <= 0;
+ ackfifo_single <= ackfifo_empty;
+ ackfifo_full <= (&acks_outstanding[LGFIFO-1:0]);
+ end
+ 2'b01: begin
+ acks_outstanding <= acks_outstanding - 1;
+ ackfifo_empty <= (acks_outstanding <= 1);
+ ackfifo_single <= (acks_outstanding == 2);
+ ackfifo_full <= 0;
+ end
+ default: begin end
+ endcase
+
+`ifdef FORMAL
+ always @(*)
+ begin
+ assert(ackfifo_single == (acks_outstanding == 1));
+ assert(ackfifo_empty == (acks_outstanding == 0));
+ assert(ackfifo_full == (acks_outstanding >= (1<<LGFIFO)));
+ assert(acks_outstanding <= (1<<LGFIFO));
+ end
+`endif
+
+ assign o_wb_stall = ackfifo_full || bus_abort || req_fifo_stall;
+ // }}}
+
+ //
+ // The return FIFO
+ // {{{
+ afifo #(
+ .OPT_REGISTER_READS(0),
+ .NFF(NFF), .LGFIFO(LGFIFO),
+ .WIDTH(2+DW)
+`ifdef FORMAL
+ , .F_OPT_DATA_STB(1'b0)
+`endif
+ ) ackfifo(.i_wclk(i_xclk_clk), .i_wr_reset_n(!xck_reset),
+ .i_wr(o_xclk_cyc && ( i_xclk_ack || i_xclk_err )),
+ .i_wr_data({ i_xclk_ack, i_xclk_err, i_xclk_data }),
+ .o_wr_full(ign_ackfifo_stall),
+ //
+ .i_rclk(i_wb_clk), .i_rd_reset_n(!bus_abort),
+ .i_rd(!no_returns),
+ .o_rd_data({ ack_stb, err_stb, ret_wb_data }),
+ .o_rd_empty(no_returns)
+`ifdef FORMAL
+ , .f_fill(ackfifo_prefill)
+`endif
+ );
+ // }}}
+
+ //
+ // Final return processing
+ // {{{
+ initial { o_wb_ack, o_wb_err } = 2'b00;
+ always @(posedge i_wb_clk)
+ if (i_reset || bus_abort || !i_wb_cyc || no_returns || o_wb_err)
+ { o_wb_ack, o_wb_err } <= 2'b00;
+ else
+ { o_wb_ack, o_wb_err } <= { ack_stb, err_stb };
+
+ always @(posedge i_wb_clk)
+ o_wb_data <= ret_wb_data;
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, req_fifo_stall, ign_ackfifo_stall,
+ ackfifo_single };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // Register/net/macro declarations
+ // {{{
+`ifdef BMC
+`define BMC_ASSERT assert
+`else
+`define BMC_ASSERT assume
+`endif
+
+ (* gclk *) reg gbl_clk;
+
+ wire [LGFIFO:0] fwb_nreqs, fwb_nacks, fwb_outstanding;
+ wire [LGFIFO:0] fxck_nreqs, fxck_nacks, fxck_outstanding;
+ reg [LGFIFO:0] ackfifo_fill, reqfifo_fill;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assume a clock
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [3:0] fwb_step, fxck_step;
+ reg [3:0] fwb_count, fxck_count;
+
+ always @(*)
+ begin
+ assume(fwb_step >= 2);
+ assume(fxck_step >= 2);
+
+ assume(fwb_step <= 4'b1000);
+ assume(fxck_step <= 4'b1000);
+
+ // assume(fwb_step == 4'b1000);
+ // assume(fxck_step == 4'b0111);
+ assume((fwb_step == 4'b0111)
+ || (fxck_step == 4'b0111));
+ end
+
+ always @(posedge gbl_clk)
+ begin
+ fwb_count <= fwb_count + fwb_step;
+ fxck_count <= fxck_count + fxck_step;
+ end
+
+ always @(*)
+ begin
+ assume(i_wb_clk == fwb_count[3]);
+ assume(i_xclk_clk == fxck_count[3]);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ....
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Cross clock stability assumptions
+ // {{{
+ always @(posedge gbl_clk)
+ if (!$rose(i_wb_clk))
+ begin
+ assume($stable(i_reset));
+
+ assume($stable(i_wb_cyc));
+ assume($stable(i_wb_stb));
+ assume($stable(i_wb_we));
+ assume($stable(i_wb_addr));
+ assume($stable(i_wb_data));
+ assume($stable(i_wb_sel));
+ end
+
+ always @(posedge gbl_clk)
+ if (!$rose(i_xclk_clk))
+ begin
+ assume($stable(i_xclk_ack));
+ assume($stable(i_xclk_err));
+ assume($stable(i_xclk_data));
+ assume($stable(i_xclk_stall));
+ end
+
+ reg past_wb_clk, past_wb_stb, past_wb_we,
+ past_wb_cyc, past_wb_reset, past_wb_err;
+ reg past_xclk, past_xclk_stall, past_xclk_ack,
+ past_xclk_err;
+ reg [DW-1:0] past_xclk_data;
+ reg f_drop_cyc_request;
+
+ always @(posedge gbl_clk)
+ begin
+ past_wb_clk <= i_wb_clk;
+ past_wb_reset<= i_reset;
+ past_wb_cyc <= i_wb_cyc;
+ past_wb_stb <= i_wb_stb;
+ past_wb_we <= i_wb_we;
+ past_wb_err <= o_wb_err;
+ end
+
+ always @(*)
+ if (!i_wb_clk || past_wb_clk)
+ begin
+ assume(past_wb_reset== i_reset);
+ assume(past_wb_cyc == i_wb_cyc);
+ assume(past_wb_stb == i_wb_stb);
+ assume(past_wb_we == i_wb_we);
+ assume(past_wb_err == o_wb_err);
+ end else begin
+ if (past_wb_err && past_wb_cyc)
+ assume(!i_wb_cyc);
+ if (fwb_outstanding > 0)
+ assume(past_wb_we == i_wb_we);
+ end
+
+ always @(posedge gbl_clk)
+ begin
+ past_xclk <= i_xclk_clk;
+ past_xclk_stall <= i_xclk_stall;
+ past_xclk_data <= i_xclk_data;
+ past_xclk_ack <= i_xclk_ack;
+ past_xclk_err <= i_xclk_err;
+ end
+
+ always @(*)
+ if (!i_xclk_clk || past_xclk)
+ begin
+ assume(past_xclk_stall == i_xclk_stall);
+ assume(past_xclk_data == i_xclk_data);
+ assume(past_xclk_ack == i_xclk_ack);
+ assume(past_xclk_err == i_xclk_err);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Wishbone bus property checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ fwb_slave #(
+ // {{{
+ .AW(AW), .DW(DW),
+ .F_LGDEPTH(LGFIFO+1), .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) slv(
+ // {{{
+ i_wb_clk, i_reset,
+ i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
+ fwb_nreqs, fwb_nacks, fwb_outstanding
+ // }}}
+ );
+
+ fwb_master #(
+ // {{{
+ .AW(AW), .DW(DW),
+ .F_LGDEPTH(LGFIFO+1), .F_OPT_DISCONTINUOUS(1),
+ .F_MAX_STALL(4),
+ .F_MAX_ACK_DELAY(7)
+ // }}}
+ ) xclkwb(
+ // {{{
+ i_xclk_clk, xck_reset,
+ o_xclk_cyc, o_xclk_stb, o_xclk_we, o_xclk_addr, o_xclk_data, o_xclk_sel,
+ i_xclk_ack, i_xclk_stall, i_xclk_data, i_xclk_err,
+ fxck_nreqs, fxck_nacks, fxck_outstanding
+ // }}}
+ );
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Random/unsorted) properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ always @(*)
+ if (reqfifo_fill != (o_xclk_stb ? 1:0) && !req_stb)
+ assert(ackfifo_fill == 0 || xclk_err_state);
+
+ always @(*)
+ reqfifo_fill = reqfifo_prefill + (o_xclk_stb ? 1:0);
+
+ always @(*)
+ ackfifo_fill = ackfifo_prefill // + (no_returns ? 0:1)
+ + ((o_wb_ack || o_wb_err) ? 1:0);
+
+ always @(*)
+ if (fwb_outstanding > 0)
+ assert(wb_active);
+
+ always @(*)
+ if (f_drop_cyc_request && !bus_abort && !xclk_err_state)
+ begin
+ // req_stb is low, so cycle line has dropped normally
+
+ // If the cycle line has since risen, there may be requests
+ // within the request FIFO in addition to the drop-CYC message.
+ if (i_wb_cyc && wb_active)
+ assert(reqfifo_fill == fwb_outstanding + 1);
+
+ // We wouldn't place a drop CYC message into the FIFO
+ // unless XCLK-CYC was already high
+ assert(o_xclk_cyc && !o_xclk_stb);
+ assert(ackfifo_fill == 0);
+ assert(fxck_outstanding == 0);
+
+ if (reqfifo_fill > 1)
+ assert(wb_active);
+ else
+ assert(!wb_active);
+ end else if (!bus_abort && xclk_err_state)
+ begin
+ //
+ // Bus error downstream causes an abort
+ assert(fxck_outstanding == 0);
+ assert(xck_reset || wb_active || !i_wb_cyc);
+ assert(!o_xclk_stb);
+ if (ackfifo_fill == 1)
+ assert(no_returns || err_stb);
+ else if (!xck_reset && ackfifo_fill == 1)
+ assert(o_wb_err);
+ end else if (!bus_abort && i_wb_cyc && !xck_reset && !xclk_err_state)
+ begin
+ //
+ // Normal operation in operation
+ //
+ assert(reqfifo_fill <= fwb_outstanding + 1);
+ assert(ackfifo_fill <= fwb_outstanding);
+ assert(fxck_outstanding <= fwb_outstanding);
+ if (o_xclk_cyc)
+ assert(wb_active || f_drop_cyc_request);
+ if (reqfifo_fill == (o_xclk_stb ? 1:0) || req_stb)
+ // Either first request is for strobe, or other
+ // request counters are valid
+ assert(reqfifo_fill + ackfifo_fill
+ + fxck_outstanding == fwb_outstanding);
+ else begin
+ // First request is to drop CYC
+ assert(reqfifo_fill== fwb_outstanding + 1);
+ assert(ackfifo_fill == 0);
+ assert(fxck_outstanding == 0);
+ assert(!o_xclk_stb);
+ assert(o_xclk_cyc);
+ end
+ if (acks_outstanding == 0)
+ assert((reqfifo_fill == 0)||!req_stb);
+ end
+
+ always @(*)
+ if (o_wb_ack && wb_active)
+ begin
+ assert(o_xclk_cyc || xclk_err_state);
+ assert(!f_drop_cyc_request);
+ assert(!xck_reset || bus_abort);
+ end
+
+ always @(*)
+ if (!bus_abort && acks_outstanding == 0)
+ assert(reqfifo_fill <= (f_drop_cyc_request ? 1:0)
+ + ((o_xclk_stb && xck_reset) ? 1:0));
+
+ always @(*)
+ if (ackfifo_fill != 0)
+ assert(o_xclk_cyc || xck_reset || xclk_err_state);
+
+ always @(*)
+ if (fxck_outstanding > fwb_outstanding)
+ assert((!i_wb_cyc && wb_active)
+ || i_reset || bus_abort || xck_reset);
+
+ always @(*)
+ if (!i_reset && xck_reset && !o_xclk_cyc)
+ assert(!i_wb_cyc || fwb_outstanding == reqfifo_fill);
+
+ always @(*)
+ if (bus_abort && i_wb_cyc)
+ assert(!wb_active);
+
+ always @(*)
+ if (acks_outstanding < (1<<LGFIFO))
+ begin
+ // assert(!reqfifo_full);
+ assert(!ackfifo_full);
+ end
+
+ always @(*)
+ if (!i_reset && !xck_reset && (fwb_outstanding > 0)
+ && ((fxck_outstanding > 0) || o_xclk_stb))
+ assert(i_wb_we == o_xclk_we);
+
+ always @(*)
+ if (!i_reset && i_wb_cyc)
+ assert(acks_outstanding == fwb_outstanding);
+
+ always @(*)
+ if (xclk_err_state)
+ assert(!o_xclk_cyc);
+
+ always @(*)
+ if (!i_reset && !bus_abort && !i_wb_cyc)
+ begin
+ if (ackfifo_empty)
+ begin
+ if (reqfifo_fill > (o_xclk_stb ? 1:0))
+ assert(!req_stb || xck_reset);
+ assert(reqfifo_fill <= 1);
+ if (xck_reset && !xck_reset_pipe)
+ assert(!o_xclk_cyc);
+ end else begin
+ // ???
+ end
+ end
+
+ always @(*)
+ f_drop_cyc_request = !req_stb
+ && (reqfifo_fill > (o_xclk_stb ? 1:0));
+
+ always @(*)
+ if (!i_reset && !xck_reset && !bus_abort && i_wb_cyc)
+ begin
+ if (!o_xclk_cyc && !xclk_err_state)
+ assert(acks_outstanding == reqfifo_fill
+ - (f_drop_cyc_request ? 1:0));
+ else if (!o_xclk_cyc && xclk_err_state)
+ assert(acks_outstanding >= reqfifo_fill
+ + ackfifo_fill);
+ else if (o_xclk_cyc && !xclk_err_state)
+ begin
+ assert(acks_outstanding >= reqfifo_fill
+ - (f_drop_cyc_request ? 1:0));
+ assert(acks_outstanding >= ackfifo_fill);
+ assert(acks_outstanding >= fxck_outstanding);
+ assert(acks_outstanding ==
+ reqfifo_fill - (((reqfifo_fill > (o_xclk_stb ? 1:0))&&(!req_stb)) ? 1:0)
+ + ackfifo_fill
+ + fxck_outstanding);
+ end
+ if (f_drop_cyc_request)
+ assert(acks_outstanding +1 == reqfifo_fill);
+ else if (reqfifo_fill == 0)
+ assert(!wb_active || o_xclk_cyc || xclk_err_state);
+ end else if (!i_reset && !xck_reset && !bus_abort && f_drop_cyc_request)
+ begin
+ assert(acks_outstanding +1 == reqfifo_fill);
+ assert(ackfifo_fill == 0);
+ assert(fxck_outstanding == 0);
+ assert(!o_xclk_stb);
+ assert(o_xclk_cyc);
+ end
+
+
+ always @(*)
+ if (!bus_abort && wb_active && reqfifo_fill == 0 && !xclk_err_state)
+ assert(o_xclk_cyc);
+
+ always @(*)
+ if (f_drop_cyc_request && !bus_abort)
+ assert(!xck_reset);
+
+ always @(*)
+ assert(!xclk_err_state || acks_outstanding != 0 || xck_reset);
+
+ always @(*)
+ if (o_xclk_cyc && !i_wb_cyc)
+ begin
+ // assert(bus_abort || !xclk_err_state);
+ if (!wb_active && !bus_abort && !xck_reset)
+ assert(f_drop_cyc_request);
+ end else if (i_wb_cyc)
+ begin
+ if (wb_active && !xck_reset)
+ assert(o_xclk_cyc || xclk_err_state
+ ||(reqfifo_fill >= acks_outstanding));
+ end
+
+ // always @(*)
+ // if (acks_outstanding >= (1<<LGFIFO))
+ // assert(o_xclk_cyc || xclk_err_state || xck_reset); // !!!!
+
+ //
+ // !!!!!!!!!!!
+ //
+ // Fig me here
+ always @(*)
+ if (wb_active && !bus_abort && !xck_reset && i_wb_cyc && !xclk_err_state)
+ begin
+ if (reqfifo_fill == 0)
+ assert(o_xclk_cyc);
+ end
+
+ always @(*)
+ if (fxck_outstanding > 0 || o_xclk_stb)
+ assert(!ign_ackfifo_stall);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Sub properties for the REQ FIFO
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ if ((acks_outstanding > 0)&&(reqfifo_fill != (o_xclk_stb ? 1:0)))
+ begin
+ // Something valuable is in the request FIFO
+ if (i_wb_cyc && !f_drop_cyc_request)
+ `BMC_ASSERT(req_we == i_wb_we);
+ else if (req_stb && o_xclk_stb)
+ `BMC_ASSERT(o_xclk_we == req_we);
+
+ // if (acks_outstanding > 0)
+ if (!o_xclk_cyc || o_xclk_stb ||
+ fxck_outstanding > 0 || ackfifo_fill > 0)
+ `BMC_ASSERT(req_stb);
+ end // else the request FIFO is empty, nothing is in it
+ // No assumptions therefore need be made
+
+ always @(*)
+ if (!bus_abort && reqfifo_fill == acks_outstanding)
+ `BMC_ASSERT(!req_fifo_stall || !f_drop_cyc_request);
+
+ always @(*)
+ if (!i_reset && o_xclk_cyc && (reqfifo_fill != (o_xclk_stb ? 1:0)))
+ `BMC_ASSERT(!req_stb || req_we == o_xclk_we
+ || fxck_outstanding == 0);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Sub properties for the ACK FIFO
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ if (!no_returns)
+ begin
+ `BMC_ASSERT(ack_stb ^ err_stb);
+
+ if ((ackfifo_fill ==(o_wb_ack ? 2:1)) && xclk_err_state)
+ `BMC_ASSERT(err_stb);
+ else if (ackfifo_fill > (o_wb_ack ? 2:1))
+ `BMC_ASSERT(!err_stb);
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg cvr_abort;
+ reg [3:0] cvr_replies, cvr_post_abort;
+
+ initial cvr_abort = 0;
+ always @(posedge i_wb_clk)
+ if (i_reset)
+ cvr_abort <= 0;
+ else if (o_wb_err && acks_outstanding > 2)
+ cvr_abort <= 1;
+
+ initial cvr_replies = 0;
+ always @(posedge i_wb_clk)
+ if (i_reset)
+ cvr_replies <= 0;
+ else if (o_wb_ack || o_wb_err)
+ cvr_replies <= cvr_replies + 1;
+
+ initial cvr_post_abort = 0;
+ always @(posedge i_wb_clk)
+ if (i_reset)
+ cvr_post_abort <= 0;
+ else if (cvr_abort && o_wb_ack)
+ cvr_post_abort <= cvr_post_abort + 1;
+
+ always @(*)
+ begin
+ cover(cvr_replies > 1); // 33
+ cover(cvr_replies > 3); // 38
+ cover(cvr_replies > 9);
+
+ cover(cvr_abort); // 31
+ cover(cvr_post_abort > 1 && cvr_replies > 1); // 63
+ cover(cvr_post_abort > 1 && cvr_replies > 2); // 63
+ cover(cvr_post_abort > 1 && cvr_replies > 3); // 65
+ cover(cvr_post_abort > 2 && cvr_replies > 3); // 65
+ cover(cvr_post_abort > 3 && cvr_replies > 3); // 68
+ cover(cvr_post_abort > 4 && cvr_replies > 3); // 70
+ cover(cvr_post_abort > 3 && cvr_replies > 6); // 72
+ end
+
+ always @(posedge gbl_clk)
+ if (!i_reset)
+ cover(cvr_replies > 9 && !i_wb_clk && acks_outstanding == 0
+ && fwb_nreqs == fwb_nacks && fwb_nreqs == cvr_replies
+ && !bus_abort && fwb_count != fxck_count);
+
+ always @(posedge gbl_clk)
+ if (!i_reset)
+ cover(cvr_replies > 9 && !i_wb_clk && acks_outstanding == 0
+ && fwb_nreqs == fwb_nacks && fwb_nreqs == cvr_replies
+ && !bus_abort && fwb_count != fxck_count
+ && fwb_step != fxck_step);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Simplifying (careless) assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // None (at present)
+
+ // }}}
+`endif
+// }}}
+endmodule