Merge FPGA repository back into UHD repository

The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.

This commit also updates the license files and paths therein.

We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.

== Original Codebase and Rebasing ==

The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/

It can be used for bisecting, reference, and a more detailed history.

The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.

If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:

- Create a directory to store patches (this should be an empty
  directory):

    mkdir ~/patches

- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:

    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge

  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.

- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:

    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches

  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.

- Go to the UHD repository and apply the patches:

    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup

== Contributors ==

The following people have contributed mainly to these files (this list
is not complete):

Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>


Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
This commit is contained in:
Martin Braun
2020-01-28 09:35:36 -08:00
co-authored by Alex Williams Andrej Rode Ashish Chaudhari Ben Hilburn Ciro Nishiguchi Daniel Jepson Derek Kozel EJ Kreinar Humberto Jimenez Ian Buckley Jörg Hofrichter Jon Kiser Josh Blum Jonathon Pendlum Matt Ettus Michael West Moritz Fischer Nick Foster Nicolas Cuervo Paul Butler Paul David Ryan Marlow Sugandha Gupta Sylvain Munaut Trung Tran Vidush Vishwanath Wade Fife
parent 74893643ca
commit 6b67702ad7
2157 changed files with 1282567 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
xsim_proj
modelsim_proj
build-ip
.ip_user_files
.Xil
xsim.dir
work/
xvlog.pb
+178
View File
@@ -0,0 +1,178 @@
# Ettus Research USRP3 FPGA Repository: Licenses
All code written by Ettus Research within this usrp3 subdirectory of the FPGA
repository is licensed as LGPLv3. This repository also contains code from other
sources, which is marked appropriately and may be licensed differently.
If you wish to obtain an alternative license on the components owned by Ettus
Research / National Instruments, please contact info@ettus.com.
## LGPL License Text
Source: https://www.gnu.org/licenses/lgpl-3.0.txt
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
Executable
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env python
#
# Copyright 2016 Ettus Research LLC
#
import argparse
import os
import subprocess
import logging
import re
import json
_LIB_DIR = os.path.join("lib")
_RFNOC_DIR = os.path.join("lib", "rfnoc")
_SIM_DIR = os.path.join("lib", "sim")
_BASE_DIR = os.path.dirname(os.path.realpath(__file__))
_SEARCH_BASE = [_RFNOC_DIR, _SIM_DIR]
_LOG = logging.getLogger(os.path.basename(__file__))
_LOG.setLevel(logging.INFO)
_STDOUT = logging.StreamHandler()
_LOG.addHandler(_STDOUT)
_FORMATTER = logging.Formatter('[%(name)s] - %(levelname)s - %(message)s')
_STDOUT.setFormatter(_FORMATTER)
def match_file(expr, path):
""" Match regex expr on all lines of path and return list of matches """
matches = []
with open(path, 'rb') as my_file:
for line in my_file:
match = expr.match(line)
if match:
matches.append(match)
return matches
def create_index(paths):
""" Create an index of modules in .v/.vhd and dump it to modules.json """
hdl_expressions = {
re.compile(r".*\.v$"):
re.compile(b"module (?P<mod_name>[\\w]+) *$", re.IGNORECASE),
re.compile(r".*\.vhd$"):
re.compile(b"entity (?P<mod_name>[\\w]+) is *$", re.IGNORECASE)
}
ignore_dirs = ["build-ip", "sim"]
modules = {}
for path in paths:
for root, dirs, files in os.walk(os.path.join(_BASE_DIR, path)):
ignore = [my_dir in dirs for my_dir in ignore_dirs]
if any(ignore):
for index, ignore_me in enumerate(ignore):
if not ignore_me:
continue
dirs.pop(dirs.index(ignore_dirs[index]))
for my_file in files:
matches = []
for key, value in hdl_expressions.items():
if key.match(my_file):
matches = match_file(value,
os.path.join(root, my_file))
break
for match in matches:
if match.group("mod_name") in modules:
_LOG.error("%s is already in modules",
match.group("mod_name"))
_LOG.error("Old Path: %s",
modules[match.group("mod_name")])
_LOG.error("New Path: %s", os.path.join(root, my_file))
else:
modules.update({
match.group("mod_name").decode('utf-8'):
os.path.join(root, my_file)
})
with open("modules.json", "w") as my_file:
json.dump(
modules, my_file, sort_keys=True, indent=4, separators=(',', ': '))
def call_xsim(path):
""" Call make xsim with default environment at path """
os.chdir(os.path.join(_BASE_DIR, path))
env = os.environ
env["REPO_BASE_PATH"] = _BASE_DIR
env["DISPLAY_NAME"] = "USRP-XSIM"
env["VIVADO_VER"] = "2017.4"
env["PRODUCT_ID_MAP"] = "foo/foo/bar/bar"
setup_env = os.path.join(_BASE_DIR, "tools", "scripts", "setupenv_base.sh")
result = subprocess.Popen(
". {setup}; make xsim".format(setup=setup_env), env=env,
shell=True).wait()
return result
def find_xsims():
""" Find testbenches in lib/sim (dirs with Makefile) """
sims = {}
for basedir in _SEARCH_BASE:
for root, _, files in os.walk(os.path.join(_BASE_DIR, basedir)):
if "Makefile" in files:
sims.update({os.path.basename(root): root})
return sims
def run_xsim(args):
""" Run xsim for all specified modules """
sims = find_xsims()
result_all = 0
if not isinstance(args.target, list):
args.target = [args.target]
if "cleanall" in args.target:
env = os.environ
env["REPO_BASE_PATH"] = _BASE_DIR
for name, path in sims.iteritems():
_LOG.info("Cleaning %s", name)
os.chdir(os.path.join(_BASE_DIR, path))
subprocess.Popen("make cleanall", env=env, shell=True).wait()
elif "all" in args.target:
for name, path in sims.iteritems():
_LOG.info("Running %s xsim", name)
result = call_xsim(path)
if result:
result_all = result
else:
for target in args.target:
_LOG.info("Running %s xsim", target)
result = call_xsim(sims[target])
if result:
result_all = result
return result_all
def parse_args():
""" Parse cmdline arguments"""
test_benches = find_xsims()
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(dest="command", metavar="")
xsim_parser = subparser.add_parser(
"xsim", help="Run available testbenches")
xsim_parser.add_argument(
"target",
nargs="+",
choices=list(test_benches.keys()) + ["all", "cleanall"],
help="Space separated simulation target(s) or all. Available targets: "
+ ", ".join(list(test_benches.keys()) + ["all", "cleanall"]),
metavar="")
index_parser = subparser.add_parser(
"index", help="Index available HDL modules")
index_parser.add_argument(
"create",
help="Create a modules.json of available HDL modules in lib/")
return parser.parse_args()
def main():
"""Main logic"""
args = parse_args()
result = 0
if args.command == "xsim":
result = run_xsim(args)
elif args.command == "index":
create_index([_LIB_DIR])
return result
if __name__ == "__main__":
exit(not main())
+71
View File
@@ -0,0 +1,71 @@
#
# Copyright 2015 Ettus Research LLC
#
# Define BASE_DIR to point to the "top" dir
BASE_DIR = $(abspath ../../../top)
# Include viv_sim_preample after defining BASE_DIR
include $(BASE_DIR)/../tools/make/viv_design_builder.mak
#-------------------------------------------------
# Design Specific
#-------------------------------------------------
ifndef PRODUCT
PRODUCT=X310
endif
ARCH=$(XIL_ARCH_${PRODUCT})
PART_ID=$(XIL_PART_ID_${PRODUCT})
# Include makefiles and sources for the DUT and its dependencies
include $(LIB_DIR)/control/Makefile.srcs
include $(LIB_DIR)/fifo/Makefile.srcs
include $(LIB_DIR)/timing/Makefile.srcs
include $(LIB_DIR)/packet_proc/Makefile.srcs
include $(LIB_DIR)/vita/Makefile.srcs
include $(LIB_DIR)/dsp/Makefile.srcs
include $(LIB_DIR)/axi/Makefile.srcs
include $(LIB_DIR)/radio/Makefile.srcs
include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
DESIGN_SRCS = $(abspath \
$(FIFO_SRCS) \
$(CONTROL_LIB_SRCS) \
$(TIMING_SRCS) \
$(PACKET_PROC_SRCS) \
$(VITA_SRCS) \
$(DSP_SRCS) \
$(AXI_SRCS) \
$(RADIO_SRCS) \
$(COREGEN_DSP_SRCS) \
)
TOP_MODULE = radio
#-------------------------------------------------
# IP Specific
#-------------------------------------------------
# If simulation contains IP, define the IP_DIR and point
# it to the base level IP directory
IP_DIR = $(BASE_DIR)/x300/ip
# Include makefiles and sources for all IP components
# *after* defining the IP_DIR
include $(IP_DIR)/fifo_short_2clk/Makefile.inc
include $(IP_DIR)/fifo_4k_2clk/Makefile.inc
DESIGN_SRCS += $(abspath \
$(IP_FIFO_4K_2CLK_SRCS) \
$(IP_FIFO_SHORT_2CLK_SRCS) \
)
# DESIGN_SRCS and VERILOG_DEFS must be defined
.DEFAULT_GOAL := netlist
netlist: .prereqs $(DESIGN_SRCS)
$(call BUILD_VIVADO_DESIGN,$(abspath ./build.tcl),$(TOP_MODULE),$(PART_ID))
clean:
@rm -rf build
@rm -rf build-ip
.PHONY: netlist clean
+8
View File
@@ -0,0 +1,8 @@
# Copyright 2015 Ettus Research
source $::env(VIV_TOOLS_DIR)/scripts/viv_utils.tcl
vivado_utils::initialize_project
vivado_utils::synthesize_design
vivado_utils::write_netlist_outputs
vivado_utils::close_batch_project
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
VIVADO_VER=CMDLINE_ARG
DISPLAY_NAME="USRP Radio"
REPO_BASE_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)
declare -A PRODUCT_ID_MAP
PRODUCT_ID_MAP["X300"]="kintex7/xc7k325t/ffg900/-2"
PRODUCT_ID_MAP["X310"]="kintex7/xc7k410t/ffg900/-2"
source $REPO_BASE_PATH/tools/scripts/setupenv_base.sh
+36
View File
@@ -0,0 +1,36 @@
#
# Copyright 2012-2013 Ettus Research LLC
# Copyright 2014 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
##################################################
# FIFO Sources
##################################################
AXI_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/axi/, \
axi_chdr_header_trigger.v \
axi_chdr_test_pattern.v \
axi_defs.v \
axi_dma_fifo.v \
axi_dma_master.v \
axi_replay.v \
axi_embed_tlast.v \
axi_extract_tlast.v \
axi_fast_extract_tlast.v \
axi_embed_tlast_tkeep.v \
axi_extract_tlast_tkeep.v \
axi_fast_fifo.v \
axi_to_strobed.v \
axis_data_swap.v \
axi_dummy.v \
strobed_to_axi.v \
axi_add_preamble.v \
axi_strip_preamble.v \
crc_xnor.v \
axis_packet_flush.v \
axis_shift_register.v \
axis_upsizer.v \
axis_downsizer.v \
axis_width_conv.v \
))
+157
View File
@@ -0,0 +1,157 @@
//
// Copyright 2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Adds preamble, EOP, and CRC/num_words check
// <preamble> <packet> <EOP> [control_chksum,word_count,payload_chksum]
// <preamble> = 64'h9E6774129E677412
// <EOP> = 64'h2A1D632F2A1D632F
module axi_add_preamble #(
parameter WIDTH=64
) (
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tlast,
input i_tvalid,
output i_tready,
//
output reg [WIDTH-1:0] o_tdata,
output o_tvalid,
input o_tready
);
function [0:0] cvita_get_has_time;
input [63:0] header;
cvita_get_has_time = header[61];
endfunction
//States
localparam IDLE = 0;
localparam PREAMBLE = 1;
localparam PASS = 3;
localparam EOP = 4;
localparam CRC = 5;
localparam PAYLOAD_WORDCOUNT_WIDTH = 16;
localparam PAYLOAD_CHKSUM_WIDTH = 32;
localparam CONTROL_CHKSUM_WIDTH = 16;
reg [2:0] state, next_state;
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] word_count;
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] cntrl_length = 16'd2;
wire [PAYLOAD_CHKSUM_WIDTH-1:0] payload_chksum;
wire [CONTROL_CHKSUM_WIDTH-1:0] control_chksum;
// Payload LFSR
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(PAYLOAD_CHKSUM_WIDTH)) payload_chksum_gen (
.clk(clk), .rst(word_count<=cntrl_length), .hold(~(i_tready && i_tvalid)),
.input_data(i_tdata), .crc_out(payload_chksum)
);
// Control LFSR
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(CONTROL_CHKSUM_WIDTH)) control_chksum_gen (
.clk(clk), .rst(word_count=='d0), .hold(~(i_tready && i_tvalid) || word_count>=cntrl_length),
.input_data(i_tdata), .crc_out(control_chksum)
);
//Update control length so control checksum is correct
always @(posedge clk) begin
if (state == IDLE && i_tvalid)
cntrl_length <= cvita_get_has_time(i_tdata) ? 16'd2 : 16'd1;
end
//Note that word_count includes EOP
always @(posedge clk) begin
if (state == IDLE) begin
word_count <= 0;
end else if (i_tready && i_tvalid || (o_tready && state == EOP)) begin
word_count <= word_count+1;
end
end
always @(posedge clk)
if (reset | clear) begin
state <= IDLE;
end else begin
state <= next_state;
end
always @(*) begin
case(state)
IDLE: begin
if (i_tvalid) begin
next_state = PREAMBLE;
end else begin
next_state = IDLE;
end
end
PREAMBLE: begin
if(o_tready) begin
next_state = PASS;
end else begin
next_state = PREAMBLE;
end
end
PASS: begin
if(i_tready && i_tvalid && i_tlast) begin
next_state = EOP;
end else begin
next_state = PASS;
end
end
EOP: begin
if(o_tready) begin
next_state = CRC;
end else begin
next_state = EOP;
end
end
CRC: begin
if(o_tready) begin
next_state = IDLE;
end else begin
next_state = CRC;
end
end
default: begin
next_state = IDLE;
end
endcase
end
//
// Muxes
//
always @*
begin
case(state)
IDLE: o_tdata = 0;
PASS: o_tdata = i_tdata;
PREAMBLE: o_tdata = 64'h9E6774129E677412;
EOP: o_tdata = 64'h2A1D632F2A1D632F;
CRC: o_tdata = {control_chksum,word_count,payload_chksum};
default: o_tdata = 0;
endcase
end
assign o_tvalid = (state == PASS) ? i_tvalid : (state != IDLE);
assign i_tready = (state == PASS) ? o_tready : 1'b0;
endmodule
+43
View File
@@ -0,0 +1,43 @@
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
module axi_chdr_header_trigger
#(
parameter WIDTH=64,
parameter SID=0
)
(input clk, input reset, input clear,
input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, input i_tready,
output trigger
);
reg state;
localparam IDLE = 0;
localparam RUN = 1;
always @(posedge clk)
if(reset | clear)
state <= IDLE;
else
case (state)
IDLE :
if(i_tvalid && i_tready)
state <= RUN;
RUN :
if(i_tready && i_tvalid && i_tlast)
state <= IDLE;
default :
state <= IDLE;
endcase // case (state)
assign trigger = i_tvalid && i_tready && (state == IDLE) && (i_tdata[15:0] != SID);
endmodule // axi_chdr_header_trigger
+505
View File
@@ -0,0 +1,505 @@
//
// Copyright 2014 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Synthesizable test pattern generator and checker
// for AXI-Stream that can be used to test transparent blocks
// (FIFOs, switches, etc)
//
module axi_chdr_test_pattern #(
parameter SR_BASE = 8'h0, //Base address for settings in this module
parameter DELAY_MODE = "DYNAMIC", //Are delays configurable at runtime {STATIC, DYNAMIC}
parameter SID_MODE = "DYNAMIC", //Is the SID configurable at runtime {STATIC, DYNAMIC}
parameter STATIC_SID = 32'h0, //SID Value if it is static
parameter BW_COUNTER = 1 //Instantiate counters to measure bandwidth (Cycles of Data Xfer / Total cycles)
) (
input clk,
input reset,
// AXI stream to hook up to input of DUT
output reg [63:0] i_tdata,
output reg i_tlast,
output reg i_tvalid,
input i_tready,
// AXI stream to hook up to output of DUT
input [63:0] o_tdata,
input o_tlast,
input o_tvalid,
output reg o_tready,
//Settings bus interface
input set_stb,
input [7:0] set_addr,
input [31:0] set_data,
// Test flags
output reg running, //Test is currently in progress
output reg done, //(Sticky) Test has finished executing
output reg [1:0] error, //Error code from last test execution
output [127:0] status_vtr, //More information about test failure.
output [95:0] bw_ratio //Bandwidth counter info
);
//
// Error Codes
//
localparam ERR_SUCCESS = 0;
localparam ERR_DATA_MISMATCH = 1;
localparam ERR_SIZE_MISMATCH_TOO_LONG = 2;
localparam ERR_SIZE_MISMATCH_TOO_SHORT = 3;
localparam ERR_TIMEOUT_LOG2 = 10;
//
// Settings
//
wire bist_size_ramp;
wire [1:0] bist_test_patt;
wire [12:0] bist_max_pkt_size;
wire bist_go, bist_cont, bist_ctrl_wr;
wire [1:0] bist_ctrl_reserved;
wire [17:0] bist_max_pkts;
wire [15:0] bist_tx_pkt_delay;
wire [7:0] bist_rx_samp_delay;
wire [31:0] bist_cvita_sid;
localparam TEST_PATT_ZERO_ONE = 2'd0;
localparam TEST_PATT_CHECKERBOARD = 2'd1;
localparam TEST_PATT_COUNT = 2'd2;
localparam TEST_PATT_COUNT_INV = 2'd3;
// SETTING: Test Control Register
// Fields:
// - [0] : (Strobe) Start the test if 1, otherwise stop a running test.
// If no test is running then reset the status. (Reseting a
// continuously running test requires two writes to this reg)
// - [1] : Start the test in continuous mode. (Run until reset or failure)
// - [3:2] : <Unused>
// - [5:4] : Test pattern:
// * 00 = Zeros and Ones (0x0000000000000000 <-> 0xFFFFFFFFFFFFFFFF)
// * 01 = Checkerboard (0x5555555555555555 <-> 0xAAAAAAAAAAAAAAAA)
// * 10 = Counter (Each byte will count up)
// * 11 = Invert Counter (Each byte will count up and invert)
setting_reg #(
.my_addr(SR_BASE + 0), .width(6), .at_reset(3'b0)
) reg_ctrl (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_test_patt, bist_ctrl_reserved, bist_cont, bist_go}),.changed(bist_ctrl_wr)
);
wire bist_start = bist_ctrl_wr & bist_go;
wire bist_clear = bist_ctrl_wr & ~bist_go;
// SETTING: Test Packet Configuration Register
// Fields:
// - [17:0] : Number of packets to transfer per BIST execution
// - [30:18] : Max number of bytes of payload per packet
// - [31] : Send variable (ramping) sized packets
setting_reg #(
.my_addr(SR_BASE + 1), .width(32), .at_reset(32'b0)
) reg_pkt_config (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_size_ramp, bist_max_pkt_size, bist_max_pkts}),.changed()
);
generate if (DELAY_MODE == "DYNAMIC") begin
// SETTING: Delay Register
// Fields:
// - [15:0] : Number of cycles to wait between generating consecutive *packets*
// - [23:16] : Number of cycles to wait between consuming consecutive *samples*
setting_reg #(
.my_addr(SR_BASE + 2), .width(24), .at_reset(24'b0)
) reg_delay (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_rx_samp_delay, bist_tx_pkt_delay}),.changed()
);
end else begin
assign {bist_rx_samp_delay, bist_tx_pkt_delay} = 24'h0;
end endgenerate
generate if (SID_MODE == "DYNAMIC") begin
// SETTING: CHDR Stream ID Register
// Fields:
// - [31:0] : Stream ID to attach to CHDR packets
setting_reg #(
.my_addr(SR_BASE + 3), .width(32), .at_reset(32'b0)
) reg_sid (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out(bist_cvita_sid),.changed()
);
end else begin
assign bist_cvita_sid = STATIC_SID;
end endgenerate
//
// State
//
localparam TX_IDLE = 3'd0;
localparam TX_START = 3'd1;
localparam TX_ACTIVE = 3'd2;
localparam TX_GAP = 3'd3;
localparam TX_DONE = 3'd4;
localparam TX_WAIT = 3'd5;
localparam RX_IDLE = 3'd0;
localparam RX_ACTIVE = 3'd1;
localparam RX_FAIL = 3'd2;
localparam RX_DONE = 3'd3;
localparam RX_WAIT = 3'd4;
reg [2:0] tx_state, rx_state;
reg [ERR_TIMEOUT_LOG2-1:0] err_timeout;
reg [1:0] test_pattern;
reg rearm_test;
reg [17:0] tx_pkt_cnt, rx_pkt_cnt;
reg [13:0] tx_byte_cnt, rx_byte_cnt;
reg [23:0] test_run_cnt;
reg [15:0] tx_delay;
reg [7:0] rx_delay;
wire [63:0] tx_cvita_hdr, rx_cvita_hdr;
wire tx_next_pkt_cond, rx_next_pkt_cond;
assign tx_next_pkt_cond = (tx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) || //Packet size reaches max OR
(bist_size_ramp && ({7'h0, tx_byte_cnt[13:3]} == tx_pkt_cnt)); //Packet size / 8 == Packet Count
assign rx_next_pkt_cond = (rx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) ||
(bist_size_ramp && ({7'h0, rx_byte_cnt[13:3]} == rx_pkt_cnt));
wire tx_test_done_cond, rx_test_done_cond;
assign tx_test_done_cond = (tx_pkt_cnt == bist_max_pkts);
assign rx_test_done_cond = (rx_pkt_cnt == bist_max_pkts);
reg [63:0] tx_data_next, rx_data_exp;
always @(*) begin
case (test_pattern)
TEST_PATT_ZERO_ONE: begin
tx_data_next <= {8{tx_byte_cnt[3] ? 8'h00 : 8'hFF}};
rx_data_exp <= {8{rx_byte_cnt[3] ? 8'h00 : 8'hFF}};
end
TEST_PATT_CHECKERBOARD: begin
tx_data_next <= {32{tx_byte_cnt[3] ? 2'b01 : 2'b10}};
rx_data_exp <= {32{rx_byte_cnt[3] ? 2'b01 : 2'b10}};
end
TEST_PATT_COUNT: begin
tx_data_next <= {8{tx_byte_cnt[10:3]}};
rx_data_exp <= {8{rx_byte_cnt[10:3]}};
end
TEST_PATT_COUNT_INV: begin
tx_data_next <= {8{(tx_byte_cnt[3] ? 8'hFF : 8'h00) ^ tx_byte_cnt[10:3]}};
rx_data_exp <= {8{(rx_byte_cnt[3] ? 8'hFF : 8'h00) ^ rx_byte_cnt[10:3]}};
end
default: begin
tx_data_next <= 64'd0;
rx_data_exp <= 64'd0;
end
endcase
end
//NOTE: We always attach the max size in the packet header for simplicity.
// This will not work with state machines that validate the packet length in the
// header with the tlast position.
assign tx_cvita_hdr = {4'h0, tx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
assign rx_cvita_hdr = {4'h0, rx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
reg [63:0] o_tdata_fail;
assign status_vtr = { //Status at the time of failure
o_tdata_fail, //[127:64]
test_run_cnt, //[63:40]
rx_data_exp[7:0], //[39:32]
rx_pkt_cnt, //[31:14]
rx_byte_cnt //[13:0]
};
//-------------------------------------------------------
// Transmitter
//-------------------------------------------------------
always @(posedge clk) begin
if (reset | (bist_clear & ~rearm_test)) begin
tx_delay <= 0;
tx_pkt_cnt <= 0;
tx_byte_cnt <= 0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
i_tvalid <= 1'b0;
tx_state <= TX_IDLE;
end else begin
case(tx_state)
TX_IDLE: begin
tx_delay <= 0;
tx_pkt_cnt <= 1;
tx_byte_cnt <= 0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
i_tvalid <= 1'b0;
// Run when bist_start asserted.
if (bist_start | rearm_test) begin
tx_state <= TX_START;
test_pattern <= bist_test_patt;
end
end // case: TX_IDLE
// START signal is asserted.
// Now need to start transmiting a packet.
TX_START: begin
// At the next clock edge drive first beat of new packet onto HDR bus.
i_tlast <= 1'b0;
i_tvalid <= 1'b1;
tx_byte_cnt <= tx_byte_cnt + 8;
i_tdata <= tx_cvita_hdr;
tx_state <= TX_ACTIVE;
end
// Valid data is (already) being driven onto the CHDR bus.
// i_tlast may also be driven asserted if current data count has reached EOP.
// Watch i_tready to see when it's consumed.
// When packets are consumed increment data counter or transition state if
// EOP has sucsesfully concluded.
TX_ACTIVE: begin
i_tvalid <= 1'b1; // Always assert tvalid
if (i_tready) begin
i_tdata <= tx_data_next;
// Will this next beat be the last in a packet?
if (tx_next_pkt_cond) begin
tx_byte_cnt <= 0;
i_tlast <= 1'b1;
tx_state <= TX_GAP;
end else begin
tx_byte_cnt <= tx_byte_cnt + 8;
i_tlast <= 1'b0;
tx_state <= TX_ACTIVE;
end
end else begin
//Keep driving all CHDR bus signals as-is until i_tready is asserted.
tx_state <= TX_ACTIVE;
end
end // case: TX_ACTIVE
// Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
// As we leave this state check if all packets in BIST sequence have been generated yet,
// and if so go to done state.
TX_GAP: begin
if (i_tready) begin
i_tvalid <= 1'b0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
tx_pkt_cnt <= tx_pkt_cnt + 1;
if (tx_test_done_cond) begin
tx_state <= TX_DONE;
end else begin
tx_state <= TX_WAIT;
tx_delay <= bist_tx_pkt_delay;
end
end else begin // if (i_tready)
tx_state <= TX_GAP;
end
end // case: TX_GAP
// Simulate inter packet gap in real UHD system
TX_WAIT: begin
if (tx_delay == 0)
tx_state <= TX_START;
else begin
tx_delay <= tx_delay - 1;
tx_state <= TX_WAIT;
end
end
// Complete test pattern BIST sequence has been transmitted.
// Sit in this state until the RX side consumes all packets except
// for when the test is running in continuous mode.
TX_DONE: begin
i_tvalid <= 1'b0;
i_tlast <= 1'b0;
i_tdata <= 64'd0;
if (running & ~rearm_test) begin
tx_state <= TX_DONE;
end else begin
tx_state <= TX_IDLE;
end
end
endcase // case (tx_state)
end
end
//-------------------------------------------------------
// Receiver
//-------------------------------------------------------
always @(posedge clk) begin
if (reset | (bist_clear & ~rearm_test)) begin
rx_delay <= 0;
rx_pkt_cnt <= 0;
rx_byte_cnt <= 0;
o_tdata_fail <= 64'h0;
o_tready <= 1'b0;
error <= ERR_SUCCESS;
done <= 1'b0;
rx_state <= RX_IDLE;
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
test_run_cnt <= 0;
end else begin
case(rx_state)
RX_IDLE: begin
rx_delay <= 0;
rx_pkt_cnt <= 1;
rx_byte_cnt <= 0;
o_tdata_fail <= 64'h0;
o_tready <= 1'b0;
error <= ERR_SUCCESS;
done <= 1'b0;
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
// Not accepting data whilst Idle,
// switch to active when packet arrives
if (o_tvalid) begin
o_tready <= 1'b1;
rx_state <= RX_ACTIVE;
end else begin
rx_state <= RX_IDLE;
end
end
RX_ACTIVE: begin
o_tready <= 1'b1;
if (o_tvalid) begin
if (o_tdata != (rx_byte_cnt == 0 ? rx_cvita_hdr : rx_data_exp)) begin
$display("axis_test_pattern: o_tdata: %x != expected: %x @ time: %d", o_tdata, rx_data_exp, $time);
error <= ERR_DATA_MISMATCH;
rx_state <= RX_FAIL;
o_tdata_fail <= o_tdata;
end else if (rx_next_pkt_cond) begin
// Last not asserted when it should be!
if (~(o_tlast === 1)) begin
$display("axis_test_pattern: o_tlast not asserted when it should be @ time: %d", $time);
error <= ERR_SIZE_MISMATCH_TOO_LONG;
rx_state <= RX_FAIL;
end else begin
// End of packet, set up to RX next
rx_byte_cnt <= 0;
rx_pkt_cnt <= rx_pkt_cnt + 1;
rx_delay <= bist_rx_samp_delay;
if (rx_test_done_cond) begin
rx_state <= rearm_test ? RX_IDLE : RX_DONE;
error <= ERR_SUCCESS;
test_run_cnt <= test_run_cnt + 1;
end else begin
rx_state <= RX_WAIT;
end
o_tready <= 1'b0;
end
end else begin
// ...last asserted when it should not be!
if (~(o_tlast === 0)) begin
$display("axis_test_pattern: o_tlast asserted when it should not be @ time: %d", $time);
error <= ERR_SIZE_MISMATCH_TOO_SHORT;
rx_state <= RX_FAIL;
end else begin
// Still in packet body
rx_byte_cnt <= rx_byte_cnt + 8;
rx_delay <= bist_rx_samp_delay;
if (bist_rx_samp_delay == 0) begin
rx_state <= RX_ACTIVE;
end else begin
rx_state <= RX_WAIT;
o_tready <= 1'b0;
end
end
end
end else begin
// Nothing to do this cycle
rx_state <= RX_ACTIVE;
end
end // case: RX_ACTIVE
// To simulate the radio consuming samples at a steady rate set by the decimation
// have a programable delay here
RX_WAIT: begin
if (rx_delay == 0) begin
rx_state <= RX_ACTIVE;
o_tready <= 1'b1;
end else begin
rx_delay <= rx_delay - 1;
rx_state <= RX_WAIT;
end
end
RX_FAIL: begin
//The test has failed but the sender still has packets en route
//Consume all of them before asserting done. Packets could be
//malformed so just blindly consume lines and count cycles of
//gaps. If non-valid cycles are more than 2^ERR_TIMEOUT_LOG2 then stop.
o_tready <= 1'b1;
if (~o_tvalid) begin
if (err_timeout == {ERR_TIMEOUT_LOG2{1'b1}}) begin
rx_state <= RX_DONE;
end
err_timeout <= err_timeout + 1;
end
end
RX_DONE: begin
o_tready <= 1'b0;
done <= 1'b1;
//The only way to exit this state is by asserting bist_clear
end
endcase // case (rx_state)
end
end
//-------------------------------------------------------
// Status Monitor
//-------------------------------------------------------
always @(posedge clk) begin
if (reset)
running <= 1'b0;
else if (tx_state == TX_START)
running <= 1'b1;
else if (rx_state == RX_DONE)
running <= 1'b0;
end
always @(posedge clk) begin
if (reset | bist_clear)
rearm_test <= 1'b0;
else if (bist_start & bist_cont)
rearm_test <= 1'b1;
else if (rx_state == RX_FAIL)
rearm_test <= 1'b0;
end
//-------------------------------------------------------
// Bandwidth Counter
//-------------------------------------------------------
generate if (BW_COUNTER) begin
reg [47:0] word_count, cyc_count;
assign bw_ratio = {word_count, cyc_count};
//Count number of lines transferred
always @(posedge clk) begin
if (reset| (bist_clear & ~rearm_test) | bist_start)
word_count <= 48'd0;
else if (o_tvalid && rx_state == RX_ACTIVE)
word_count <= word_count + 48'd1;
end
//Count cycles as long as test is running
always @(posedge clk) begin
if (reset| (bist_clear & ~rearm_test) | bist_start)
cyc_count <= 48'd0;
else if (rx_state == RX_ACTIVE || rx_state == RX_WAIT)
cyc_count <= cyc_count + 48'd1;
end
end else begin
assign bw_ratio = 96'h0;
end endgenerate
endmodule
+40
View File
@@ -0,0 +1,40 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// AXI4 Burst enumeration
//
`define AXI4_BURST_FIXED 2'b00
`define AXI4_BURST_INCR 2'b01
`define AXI4_BURST_WRAP 2'b10
`define AXI4_BURST_RSVD 2'b11
//
// AXI4 response code enumeration
//
`define AXI4_RESP_OKAY 2'b00
`define AXI4_RESP_EXOKAY 2'b01
`define AXI4_RESP_SLVERR 2'b10
`define AXI4_RESP_DECERR 2'b11
//
// AXI4 lock enumeration
//
`define AXI4_LOCK_NORMAL 1'b0
`define AXI4_LOCK_EXCLUSIVE 1'b1
//
// AXI4 memory attrubutes
//
`define AXI4_CACHE_ALLOCATE 4'h8
`define AXI4_CACHE_OTHER_ALLOCATE 4'h4
`define AXI4_CACHE_MODIFIABLE 4'h2
`define AXI4_CACHE_BUFFERABLE 4'h1
//
// AXI4 PROT attributes
//
`define AXI4_PROT_PRIVILEDGED 3'h1
`define AXI4_PROT_NON_SECURE 3'h2
`define AXI4_PROT_INSTRUCTION 3'h4
File diff suppressed because it is too large Load Diff
+548
View File
@@ -0,0 +1,548 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`include "axi_defs.v"
`define DEBUG if (0)
module axi_dma_master #(
parameter AWIDTH = 32,
parameter DWIDTH = 64
) (
input aclk, // Global AXI clock
input areset, // Global AXI reset
//
// AXI Write address channel
//
output [0 : 0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
output reg [AWIDTH-1 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
output reg [7 : 0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
output [1 : 0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
output [0 : 0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
output [3 : 0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
output [2 : 0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
output [3 : 0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
output [3 : 0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
output [0 : 0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
output reg m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
input m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
//
// AXI Write data channel.
//
output [DWIDTH-1 : 0] m_axi_wdata, // Write data
output [DWIDTH/8-1 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
output reg m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
output m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
input m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
//
// AXI Write response channel signals
//
input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
input [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
input [0 : 0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
input m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
output reg m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
//
// AXI Read address channel
//
output [0 : 0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
output reg [AWIDTH-1 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
output reg [7 : 0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
output [2 : 0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
output [1 : 0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
output [0 : 0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
output [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
output [3 : 0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
output [3 : 0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
output [0 : 0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
output reg m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
input m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
//
// AXI Read data channel
//
input [0 : 0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
input [DWIDTH-1 : 0] m_axi_rdata, // Read data.
input [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
input m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
input [0 : 0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
output m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
//
// DMA interface for Write transaction
//
input [AWIDTH-1:0] write_addr, // Byte address for start of write transaction (should be 64bit alligned)
input [7:0] write_count, // Count of 64bit words to write. (minus one)
input write_ctrl_valid,
output reg write_ctrl_ready,
input [DWIDTH-1:0] write_data,
input write_data_valid,
output write_data_ready,
//
// DMA interface for Read
//
input [AWIDTH-1:0] read_addr, // Byte address for start of read transaction (should be 64bit alligned)
input [7:0] read_count, // Count of 64bit words to read.
input read_ctrl_valid,
output reg read_ctrl_ready,
output [DWIDTH-1:0] read_data,
output read_data_valid,
input read_data_ready,
//
// Debug Bus
//
output [31:0] debug
);
localparam AW_IDLE = 0;
localparam WAIT_AWREADY = 1;
localparam WAIT_BVALID = 2;
localparam AW_ERROR = 3;
reg [1:0] write_addr_state;
reg [7:0] write_data_count; // Count write transfers.
reg enable_data_write;
localparam DW_IDLE = 0;
localparam DW_RUN = 1;
localparam DW_LAST = 2;
reg [1:0] write_data_state;
localparam AR_IDLE = 0;
localparam WAIT_ARREADY = 1;
localparam WAIT_READ_DONE = 2;
localparam AR_ERROR = 3;
reg [1:0] read_addr_state;
localparam DR_IDLE = 0;
localparam DR_RUN = 1;
localparam DR_WAIT_ERROR = 2;
localparam DR_ERROR = 3;
reg [1:0] read_data_state;
reg [7:0] read_data_count;
reg enable_data_read;
///////////////////////////
// DEBUG
///////////////////////////
assign debug= {24'h0,write_addr_state[1:0],write_data_state[1:0],read_addr_state[1:0],read_data_state[1:0]};
//
//
//
/////////////////////////////////////////////////////////////////////////////////
//
// AXI Write address channel
//
/////////////////////////////////////////////////////////////////////////////////
assign m_axi_awid = 1'b0;
assign m_axi_awsize = $clog2(DWIDTH/8);
assign m_axi_awburst = `AXI4_BURST_INCR;
assign m_axi_awlock = `AXI4_LOCK_NORMAL;
assign m_axi_awcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
assign m_axi_awprot = `AXI4_PROT_NON_SECURE;
assign m_axi_awqos = 4'h0;
assign m_axi_awregion = 4'h0;
assign m_axi_awuser = 1'b0;
//
// AXI Write address state machine
//
always @(posedge aclk)
if (areset) begin
write_ctrl_ready <= 1'b0;
write_addr_state <= AW_IDLE;
m_axi_awaddr <= {AWIDTH{1'b0}};
m_axi_awlen[7:0] <= 8'h0;
m_axi_awvalid <= 1'b0;
m_axi_bready <= 1'b0;
end else
case (write_addr_state)
//
// AW_IDLE
// We are ready to accept a new write transaction.
//
AW_IDLE: begin
// Premptively accept new write transaction since we are idle.
write_ctrl_ready <= 1'b1;
// No need to be waiting for a response while idle.
m_axi_bready <= 1'b0;
// If we are offered a new transaction then.....
if (write_ctrl_valid) begin
// Drive all the relevent AXI4 write address channel signals next cycle.
m_axi_awaddr <= write_addr;
m_axi_awlen[7:0] <= {write_count};
m_axi_awvalid <= 1'b1;
// If the AXI4 write channel is pre-emptively accepting the transaction...
if (m_axi_awready == 1'b1) begin
// ...go straight to looking for a transaction response...
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",write_addr,write_count,$time);
write_addr_state <= WAIT_BVALID;
m_axi_bready <= 1'b1;
end else begin
// ...otherwise wait to get the transaction accepted.
write_addr_state <= WAIT_AWREADY;
end
end
end
//
// WAIT_AWREADY
// Waiting for AXI4 slave to accept new write transaction.
//
WAIT_AWREADY: begin
write_ctrl_ready <= 1'b0;
// If the AXI4 write channel is accepting the transaction...
if (m_axi_awready == 1'b1) begin
// ...go to looking for a transaction response...
write_addr_state <= WAIT_BVALID;
m_axi_awvalid <= 1'b0;
m_axi_bready <= 1'b1;
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_awaddr,m_axi_awlen[7:0],$time);
end else begin
// ...otherwise wait to get the trasaction accepted.
write_addr_state <= WAIT_AWREADY;
end
end // case: WAIT_AWREADY
//
// WAIT_BVALID
// Write transaction has been accepted, now waiting for a response to signal it's sucsesful.
// Ignoring ID tag for the moment
//
WAIT_BVALID: begin
write_ctrl_ready <= 1'b0;
m_axi_awvalid <= 1'b0;
// Wait for response channel to signal how write transaction went down....
if (m_axi_bvalid == 1'b1) begin
if ((m_axi_bresp == `AXI4_RESP_OKAY) || (m_axi_bresp == `AXI4_RESP_EXOKAY)) begin
// ....it went well, we are ready to start something new.
write_addr_state <= AW_IDLE;
m_axi_bready <= 1'b0;
write_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
end else if ((m_axi_bresp == `AXI4_RESP_SLVERR) || (m_axi_bresp == `AXI4_RESP_DECERR)) begin
// ....things got ugly, retreat to an error stat and wait for intervention.
write_addr_state <= AW_ERROR;
m_axi_bready <= 1'b0;
end
end else begin
write_addr_state <= WAIT_BVALID;
m_axi_bready <= 1'b1;
end
end // case: WAIT_BVALID
//
// AW_ERROR
// Something bad happened, going to need external intervention to restore a safe state.
//
AW_ERROR: begin
write_ctrl_ready <= 1'b0;
write_addr_state <= AW_ERROR;
m_axi_awaddr <= {AWIDTH{1'b0}};
m_axi_awlen[7:0] <= 8'h0;
m_axi_awvalid <= 1'b0;
m_axi_bready <= 1'b0;
end
endcase // case(write_addr_state)
/////////////////////////////////////////////////////////////////////////////////
//
// AXI Write data channel
//
/////////////////////////////////////////////////////////////////////////////////
assign m_axi_wstrb = {DWIDTH/8{1'b1}};
assign m_axi_wuser = 1'b0;
//
// AXI Write data state machine
//
always @(posedge aclk)
if (areset) begin
write_data_state <= AW_IDLE;
write_data_count <= 1;
enable_data_write <= 1'b0;
m_axi_wlast <= 1'b0;
end else
case (write_data_state)
//
// DW_IDLE
// Sit in this state until presented with the control details of a new write transaction.
//
DW_IDLE: begin
write_data_count <= 1;
m_axi_wlast <= 1'b0;
if (write_ctrl_valid && write_ctrl_ready) begin
enable_data_write <= 1'b1;
if (write_count[7:0] == 8'h0) begin
// Single transfer transaction
write_data_state <= DW_LAST;
m_axi_wlast <= 1'b1;
end else begin
write_data_state <= DW_RUN;
end
end else begin
write_data_state <= DW_IDLE;
end
end
//
// DW_RUN
//
DW_RUN : begin
enable_data_write <= 1'b1;
m_axi_wlast <= 1'b0;
if (write_data_valid && m_axi_wready) begin
// Single write transfer
write_data_count <= write_data_count + 1;
if (write_data_count == m_axi_awlen[7:0]) begin
write_data_state <= DW_LAST;
m_axi_wlast <= 1'b1;
end else begin
write_data_state <= DW_RUN;
end
end else begin
write_data_state <= DW_RUN;
end
end
//
// DW_LAST
//
DW_LAST: begin
if (write_data_valid && m_axi_wready) begin
enable_data_write <= 1'b0;
write_data_state <= DW_IDLE;
m_axi_wlast <= 1'b0;
end else begin
enable_data_write <= 1'b1;
write_data_state <= DW_LAST;
m_axi_wlast <= 1'b1;
end
end // case: DW_LAST
//
default:
write_data_state <= DW_IDLE;
endcase // case(write_data_state)
assign m_axi_wdata = write_data;
assign m_axi_wvalid = enable_data_write && write_data_valid;
assign write_data_ready = enable_data_write && m_axi_wready;
/////////////////////////////////////////////////////////////////////////////////
//
// AXI Read address channel
//
/////////////////////////////////////////////////////////////////////////////////
assign m_axi_arid = 1'b0;
assign m_axi_arsize = $clog2(DWIDTH/8);
assign m_axi_arburst = `AXI4_BURST_INCR;
assign m_axi_arlock = `AXI4_LOCK_NORMAL;
assign m_axi_arcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
assign m_axi_arprot = `AXI4_PROT_NON_SECURE;
assign m_axi_arqos = 4'h0;
assign m_axi_arregion = 4'h0;
assign m_axi_aruser = 1'b0;
//
// AXI Read address state machine
//
always @(posedge aclk)
if (areset) begin
read_ctrl_ready <= 1'b0;
read_addr_state <= AR_IDLE;
m_axi_araddr <= {AWIDTH{1'b0}};
m_axi_arlen[7:0] <= 8'h0;
m_axi_arvalid <= 1'b0;
end else
case (read_addr_state)
//
// AR_IDLE
// We are ready to accept a new read transaction.
//
AR_IDLE: begin
// Premptively accept new read transaction since we are idle.
read_ctrl_ready <= 1'b1;
// If we are offered a new transaction then.....
if (read_ctrl_valid) begin
// Drive all the relevent AXI4 read address channel signals next cycle.
m_axi_araddr <= read_addr;
m_axi_arlen[7:0] <= {read_count};
m_axi_arvalid <= 1'b1;
// If the AXI4 read channel is pre-emptively accepting the transaction...
if (m_axi_arready == 1'b1) begin
// ...go straight to looking for the transaction to complete
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",read_addr,read_count,$time);
read_addr_state <= WAIT_READ_DONE;
end else begin
// ...otherwise wait to get the transaction accepted.
read_addr_state <= WAIT_ARREADY;
end
end
end
//
// WAIT_ARREADY
// Waiting for AXI4 slave to accept new read transaction.
//
WAIT_ARREADY: begin
read_ctrl_ready <= 1'b0;
// If the AXI4 read channel is accepting the transaction...
if (m_axi_arready == 1'b1) begin
// ...go to looking for the transaction to complete...
read_addr_state <= WAIT_READ_DONE;
m_axi_arvalid <= 1'b0;
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_araddr,m_axi_arlen[7:0],$time);
end else begin
// ...otherwise wait to get the trasaction accepted.
read_addr_state <= WAIT_ARREADY;
end
end // case: WAIT_ARREADY
//
// WAIT_READ_DONE
// Read transaction has been accepted, now waiting for the data transfer to complete
// Ignoring ID tag for the moment
//
WAIT_READ_DONE: begin
read_ctrl_ready <= 1'b0;
m_axi_arvalid <= 1'b0;
// Wait for read transaction to complete
if (read_data_state == DR_IDLE) begin
// ....it went well, we are ready to start something new.
read_addr_state <= AR_IDLE;
read_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
end else if (read_data_state == DR_ERROR) begin
// ....things got ugly, retreat to an error stat and wait for intervention.
read_addr_state <= AR_ERROR;
end else begin
read_addr_state <= WAIT_READ_DONE;
end
end // case: WAIT_BVALID
//
// AR_ERROR
// Something bad happened, going to need external intervention to restore a safe state.
//
AR_ERROR: begin
read_ctrl_ready <= 1'b0;
read_addr_state <= AR_ERROR;
m_axi_araddr <= {AWIDTH{1'b0}};
m_axi_arlen[7:0] <= 8'h0;
m_axi_arvalid <= 1'b0;
end
endcase // case(read_addr_state)
/////////////////////////////////////////////////////////////////////////////////
//
// AXI Read data channel
//
/////////////////////////////////////////////////////////////////////////////////
//
// AXI Read data state machine
//
always @(posedge aclk)
if (areset) begin
read_data_state <= AR_IDLE;
read_data_count <= 0;
enable_data_read <= 1'b0;
end else
case (read_data_state)
//
// DR_IDLE
// Sit in this state until presented with the control details of a new read transaction.
//
DR_IDLE: begin
read_data_count <= 0;
if (read_ctrl_valid && read_ctrl_ready) begin
enable_data_read <= 1'b1;
read_data_state <= DR_RUN;
end else begin
read_data_state <= DR_IDLE;
end
end
//
// DR_RUN
// Sit here counting read transfers. If any have error's shift to error state.
//
DR_RUN : begin
enable_data_read <= 1'b1;
if (read_data_ready && m_axi_rvalid) begin
// Single read transfer
read_data_count <= read_data_count + 1;
if ((m_axi_rresp == `AXI4_RESP_SLVERR) || (m_axi_rresp == `AXI4_RESP_DECERR)) begin
if (m_axi_rlast) begin
read_data_state <= DR_ERROR;
end else begin
read_data_state <= DR_WAIT_ERROR;
end
end else if (m_axi_rlast) begin // Implicitly good response signalled this transfer.
if (read_data_count == m_axi_arlen[7:0]) begin
read_data_state <= DR_IDLE;
end else begin
read_data_state <= DR_ERROR;
end
end else begin
read_data_state <= DR_RUN;
end
end else begin
read_data_state <= DR_RUN;
end
end
//
// DR_WAIT_ERROR
// Something bad happened, wait for last signalled in this burst
//
DR_WAIT_ERROR: begin
if (read_data_ready && m_axi_rvalid && m_axi_rlast) begin
enable_data_read <= 1'b0;
read_data_state <= DR_ERROR;
end else begin
enable_data_read <= 1'b1;
read_data_state <= DR_WAIT_ERROR;
end
end // case: DR_WAIT_ERROR
//
// DR_ERROR
// Something bad happened, going to need external intervention to restore a safe state.
//
DR_ERROR: begin
enable_data_read <= 1'b0;
read_data_state <= DR_ERROR;
end // case: DR_ERROR
endcase // case(read_data_state)
assign read_data = m_axi_rdata;
assign m_axi_rready = enable_data_read && read_data_ready;
assign read_data_valid = enable_data_read && m_axi_rvalid;
endmodule // axi_dma_master
+85
View File
@@ -0,0 +1,85 @@
//
// Copyright 2015 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module axi_dummy
(
// sys connect
input s_axi_aclk,
input s_axi_areset,
// axi4 lite slave port
input [31:0] s_axi_awaddr,
input s_axi_awvalid,
output s_axi_awready,
input [31:0] s_axi_wdata,
input [3:0] s_axi_wstrb,
input s_axi_wvalid,
output s_axi_wready,
output [1:0] s_axi_bresp,
output s_axi_bvalid,
input s_axi_bready,
input [31:0] s_axi_araddr,
input s_axi_arvalid,
output s_axi_arready,
output [31:0] s_axi_rdata,
output [1:0] s_axi_rresp,
output s_axi_rvalid,
input s_axi_rready
);
parameter DEC_ERR = 1'b1;
localparam IDLE = 3'b001;
localparam READ_IN_PROGRESS = 3'b010;
localparam WRITE_IN_PROGRESS = 3'b100;
reg [2:0] state;
always @ (posedge s_axi_aclk) begin
if (s_axi_areset) begin
state <= IDLE;
end
else case (state)
IDLE: begin
if (s_axi_arvalid)
state <= READ_IN_PROGRESS;
else if (s_axi_awvalid)
state <= WRITE_IN_PROGRESS;
end
READ_IN_PROGRESS: begin
if (s_axi_rready)
state <= IDLE;
end
WRITE_IN_PROGRESS: begin
if (s_axi_bready)
state <= IDLE;
end
default: begin
state <= IDLE;
end
endcase
end
assign s_axi_awready = (state == IDLE);
assign s_axi_wready = (state == WRITE_IN_PROGRESS);
assign s_axi_bvalid = (state == WRITE_IN_PROGRESS);
assign s_axi_arready = (state == IDLE);
assign s_axi_rdata = 32'hdead_ba5e;
assign s_axi_rvalid = (state == READ_IN_PROGRESS);
assign s_axi_rresp = DEC_ERR ? 2'b11 : 2'b00;
assign s_axi_bresp = DEC_ERR ? 2'b11 : 2'b00;
endmodule
+132
View File
@@ -0,0 +1,132 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
// a multi-word sequence to encode it (and the escape character when it appears as data input).
//
// 0x1234567887654321 with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
//
// 0xDEADBEEFFEEDCAFE with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
//
// 0xDEADBEEFFEEDCAFE without last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
//
module axi_embed_tlast #(
parameter WIDTH=64,
parameter ADD_CHECKSUM=0
) (
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tlast,
input i_tvalid,
output i_tready,
//
output reg [WIDTH-1:0] o_tdata,
output o_tvalid,
input o_tready
);
localparam PASS = 0;
localparam ZERO = 1;
localparam ONE = 2;
localparam ESCAPE = 3;
localparam IDLE = 0;
localparam LAST = 1;
localparam ESC = 2;
localparam FINISH = 3;
reg [1:0] state, next_state;
reg [1:0] select;
wire [31:0] checksum;
generate if (ADD_CHECKSUM == 1) begin
reg [31:0] checksum_reg;
always @(posedge clk) begin
if (reset | clear) begin
checksum_reg <= 0;
end else if (i_tready && i_tvalid && i_tlast) begin
checksum_reg <= 0;
end else if (i_tready && i_tvalid) begin
checksum_reg <= checksum_reg ^ i_tdata[31:0] ^ i_tdata[63:32];
end
end
assign checksum = checksum_reg;
end else begin
assign checksum = 32'h0;
end endgenerate
always @(posedge clk)
if (reset | clear) begin
state <= IDLE;
end else begin if (o_tready)
state <= next_state;
end
always @(*) begin
case(state)
IDLE: begin
if (i_tlast && i_tvalid) begin
next_state = LAST;
select = ESCAPE;
end else if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
next_state = ESC;
select = ESCAPE;
end else begin
next_state = IDLE;
select = PASS;
end
end // case: IDLE
LAST: begin
select = ONE;
next_state = FINISH;
end
ESC: begin
select = ZERO;
next_state = FINISH;
end
FINISH: begin
select = PASS;
if (i_tvalid)
next_state = IDLE;
else
next_state = FINISH;
end
endcase // case(state)
end // always @ (*)
//
// Muxes
//
always @*
begin
case(select)
PASS: o_tdata = i_tdata;
ZERO: o_tdata = 0;
ONE: o_tdata = {checksum[31:0],32'h1};
ESCAPE: o_tdata = 64'hDEADBEEFFEEDCAFE;
endcase // case(select)
end
assign o_tvalid = (select == PASS) ? i_tvalid : 1'b1;
assign i_tready = (select == PASS) ? o_tready : 1'b0;
endmodule // axi_embed_tlast
+152
View File
@@ -0,0 +1,152 @@
//
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_embed_tlast_tkeep
//
// Description:
//
// This module takes the TLAST and TKEEP values of an AXI-Stream interface
// and embeds them into the data stream. This allows a data pipe to be used
// that isn't wide enough for the TDATA, TLAST,and TKEEP to be passed through
// in parallel. Since TLAST and TKEEP are only usually needed for one word
// per packet, this also reduces the amount of memory required to store a
// packet. Note that this module only supports TKEEP at the end of a packet
// when TLAST is asserted. See also axi_extract_tlast_tkeep.
//
// This embedding is accomplished by using an escape sequence using the word
// 0xDEADBEEF as the escape code. If TLAST and TKEEP are both 0 (the usual
// case) then no escape sequence is used. Any word that has "DEADBEEF" in the
// most significant position is considered an escape word. The least
// significant bits of the escape word contain the TKEEP and TLAST bits. The
// word following the escape word is the normal data word associated with
// those TLAST and TKEEP values.
//
// Here are some examples for the case where DATA_W = 64
//
// 0x1234567887654321 with TLAST=0 and TKEEP=0 becomes
// 0x1234567887654321
//
// 0x1234567887654321 with TLAST=1 and TKEEP=0 becomes
// 0xDEADBEEF00000001 0x1234567887654321
//
// 0x1234567887654321 with TLAST=1 and TKEEP=2 becomes
// 0xDEADBEEF00000005 0x1234567887654321
//
// 0x1234567887654321 with TLAST=0 and TKEEP=1 becomes
// 0x1234567887654321 (because TKEEP is ignored when TLAST=0)
//
// 0xDEADBEEFFEEDCAFE without TLAST=0 and TKEEP=0 becomes
// 0xDEADBEEF00000000 0xDEADBEEFFEEDCAFE
//
// 0xDEADBEEFFEEDCAFE with TLAST=0 and TKEEP=1 becomes
// 0xDEADBEEF00000002 0xDEADBEEFFEEDCAFE
//
module axi_embed_tlast_tkeep #(
parameter DATA_W = 64,
parameter KEEP_W = DATA_W/8
) (
input clk,
input rst,
// Input AXI-Stream
input [DATA_W-1:0] i_tdata,
input [KEEP_W-1:0] i_tkeep,
input i_tlast,
input i_tvalid,
output i_tready,
// Output AXI-Stream
output reg [DATA_W-1:0] o_tdata,
output o_tvalid,
input o_tready
);
localparam ESC_WORD_W = 32;
localparam [ESC_WORD_W-1:0] ESC_WORD = 'hDEADBEEF;
//---------------------------------------------------------------------------
// Parameter Checking
//---------------------------------------------------------------------------
if (DATA_W < ESC_WORD_W+KEEP_W+1) begin : gen_assertion
// Cause an error if DATA_W is not large enough.
DATA_W_is_not_large_enough_to_store_escape_code_TKEEP_and_TLAST();
end
//---------------------------------------------------------------------------
// State Machine
//---------------------------------------------------------------------------
localparam PASS = 0;
localparam ESCAPE = 1;
localparam ST_IDLE = 0;
localparam ST_DATA = 1;
reg [0:0] state = ST_IDLE;
reg [0:0] next_state;
reg [0:0] select;
always @(posedge clk) begin
if (rst) begin
state <= ST_IDLE;
end else begin if (o_tready)
state <= next_state;
end
end
always @(*) begin
case(state)
ST_IDLE: begin
if (i_tlast && i_tvalid) begin
next_state = ST_DATA;
select = ESCAPE;
end else if ((i_tdata[DATA_W-1 -: ESC_WORD_W] == ESC_WORD) && i_tvalid) begin
next_state = ST_DATA;
select = ESCAPE;
end else begin
next_state = ST_IDLE;
select = PASS;
end
end
ST_DATA: begin
select = PASS;
if (i_tvalid) begin
next_state = ST_IDLE;
end else begin
next_state = ST_DATA;
end
end
endcase
end
//---------------------------------------------------------------------------
// Output Multiplexers
//---------------------------------------------------------------------------
always @(*) begin
case(select)
PASS : begin
o_tdata = i_tdata;
end
ESCAPE : begin
o_tdata = {DATA_W{1'b0}};
o_tdata[DATA_W-1 -: ESC_WORD_W] = ESC_WORD;
o_tdata[ 1 +: KEEP_W] = i_tkeep;
o_tdata[ 0 +: 1] = i_tlast;
end
endcase
end
assign o_tvalid = (select == PASS) ? i_tvalid : 1'b1;
assign i_tready = (select == PASS) ? o_tready : 1'b0;
endmodule
+147
View File
@@ -0,0 +1,147 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
// a multi-word sequence to encode it (and the escape character when it appears as data input).
//
// 0x1234567887654321 with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
//
// 0xDEADBEEFFEEDCAFE with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
//
// 0xDEADBEEFFEEDCAFE without last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
//
module axi_extract_tlast #(
parameter WIDTH=64,
parameter VALIDATE_CHECKSUM=0
) (
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tvalid,
output reg i_tready,
//
output [WIDTH-1:0] o_tdata,
output reg o_tlast,
output reg o_tvalid,
input o_tready,
//
output reg checksum_error
);
reg [1:0] state, next_state;
localparam IDLE = 0;
localparam EXTRACT1 = 1;
localparam EXTRACT2 = 2;
localparam EXTRACT3 = 3;
assign o_tdata = i_tdata;
reg checksum_error_pre;
reg [31:0] checksum, old_checksum;
always @(posedge clk)
if (reset | clear) begin
checksum <= 0;
old_checksum <= 0;
end else if (VALIDATE_CHECKSUM && o_tready && i_tvalid && o_tlast) begin
checksum <= 0;
old_checksum <= 0;
end else if (VALIDATE_CHECKSUM && i_tready && i_tvalid && (state == IDLE)) begin
checksum <= checksum ^ i_tdata[31:0] ^ i_tdata[63:32];
old_checksum <= checksum;
end
always @(posedge clk)
checksum_error <= checksum_error_pre;
always @(posedge clk)
if (reset | clear) begin
state <= IDLE;
end else begin
state <= next_state;
end
always @(*) begin
checksum_error_pre = 0;
case(state)
//
// Search for Escape sequence "0xDEADBEEFFEEDCAFE"
// If ESC found don't pass data downstream but transition to next state.
// else pass data downstream.
//
IDLE: begin
o_tlast = 1'b0;
if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
next_state = EXTRACT1;
o_tvalid = 1'b0;
i_tready = 1'b1;
end else begin
next_state = IDLE;
o_tvalid = i_tvalid;
i_tready = o_tready;
end // else: !if((i_tdata == 'hDEADBEEFFEEDCAFE) && i_tvalid)
end // case: IDLE
//
// Look at next data. If it's a 0x1 then o_tlast should be asserted with next data word.
// if it's 0x0 then it signals emulation of the Escape code in the original data stream
// and we should just pass the next data word through unchanged with no o_tlast indication.
//
EXTRACT1: begin
o_tvalid = 1'b0;
i_tready = 1'b1;
o_tlast = 1'b0;
if (i_tvalid) begin
if (i_tdata[31:0] == 'h1) begin
if (VALIDATE_CHECKSUM && (old_checksum != i_tdata[63:32]))
checksum_error_pre = 1'b1;
next_state = EXTRACT2;
end else begin
// We assume emulation and don't look for illegal codes.
next_state = EXTRACT3;
end // else: !if(i_tdata == 'h1)
end else begin // if (i_tvalid)
next_state = EXTRACT1;
end // else: !if(i_tvalid)
end // case: EXTRACT1
//
// Assert o_tlast with data word.
//
EXTRACT2: begin
o_tvalid = i_tvalid;
i_tready = o_tready;
o_tlast = 1'b1;
if (i_tvalid & o_tready)
next_state = IDLE;
else
next_state = EXTRACT2;
end
//
// Emulation, don't assert o_tlast with dataword.
//
EXTRACT3: begin
o_tvalid = i_tvalid;
i_tready = o_tready;
o_tlast = 1'b0;
if (i_tvalid & o_tready)
next_state = IDLE;
else
next_state = EXTRACT2;
end
endcase // case(state)
end
endmodule // axi_extract_tlast
+128
View File
@@ -0,0 +1,128 @@
//
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_extract_tlast_tkeep
//
// Description:
//
// This module extracts the TLAST and TKEEP values that were embedded by the
// axi_embed_tlast_tkeep module. See axi_embed_tlast_tkeep for a description
// of how the data is encoded.
//
// Here are some extraction examples for DATA_W = 64.
//
// 0x1234567887654321 becomes
// 0x1234567887654321 (no changes)
//
// 0xDEADBEEF00000001 0x1234567887654321 becomes
// 0x1234567887654321 with TLAST=1 and TKEEP=0
//
// 0xDEADBEEF00000005 0x1234567887654321 becomes
// 0x1234567887654321 with TLAST=1 and TKEEP=2
//
// 0xDEADBEEF00000000 0xDEADBEEFFEEDCAFE
// 0xDEADBEEFFEEDCAFE without TLAST=0 and TKEEP=0 becomes
//
// 0xDEADBEEF00000002 0xDEADBEEFFEEDCAFE
// 0xDEADBEEFFEEDCAFE with TLAST=0 and TKEEP=1 becomes
//
module axi_extract_tlast_tkeep #(
parameter DATA_W = 64,
parameter KEEP_W = DATA_W /8
) (
input clk,
input rst,
// Input AXI-Stream
input [DATA_W-1:0] i_tdata,
input i_tvalid,
output reg i_tready,
// Output AXI-Stream
output reg [DATA_W-1:0] o_tdata,
output reg [KEEP_W-1:0] o_tkeep,
output reg o_tlast,
output reg o_tvalid,
input o_tready
);
localparam ESC_WORD_W = 32;
localparam [ESC_WORD_W-1:0] ESC_WORD = 'hDEADBEEF;
//---------------------------------------------------------------------------
// TKEEP and TLAST Holding Register
//---------------------------------------------------------------------------
reg save_flags;
reg tlast_saved;
reg [KEEP_W-1:0] tkeep_saved;
always @(posedge clk) begin
if (save_flags) begin
// Save the TLAST and TKEEP values embedded in the escape word
tlast_saved <= i_tdata[0];
tkeep_saved <= i_tdata[1 +: KEEP_W];
end
end
//--------------------------------------------------------------------------
// State Machine
//--------------------------------------------------------------------------
localparam ST_IDLE = 0;
localparam ST_DATA = 1;
reg [0:0] state = ST_IDLE;
reg [0:0] next_state;
always @(posedge clk) begin
if (rst) begin
state <= ST_IDLE;
end else begin
state <= next_state;
end
end
always @(*) begin
// Default assignments (pass through)
o_tdata = i_tdata;
o_tlast = 1'b0;
o_tkeep = {KEEP_W{1'b1}};
save_flags = 1'b0;
next_state = state;
o_tvalid = i_tvalid;
i_tready = o_tready;
case(state)
//
// Search for escape code. If found don't pass data downstream but
// transition to next state. Otherwise, pass data downstream.
//
ST_IDLE: begin
if ((i_tdata[DATA_W-1 -: ESC_WORD_W] == ESC_WORD) && i_tvalid) begin
save_flags = 1'b1;
next_state = ST_DATA;
o_tvalid = 1'b0;
i_tready = 1'b1;
end
end
//
// Output data word with the saved TLAST and TKEEP values
//
ST_DATA: begin
o_tlast = tlast_saved;
o_tkeep = tkeep_saved;
if (i_tvalid & o_tready) begin
next_state = ST_IDLE;
end
end
endcase
end
endmodule
+193
View File
@@ -0,0 +1,193 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Ultra fast critical path FIFO.
// Only 2 entrys but no combinatorial feed through paths
//
module axi_fast_extract_tlast
#(parameter WIDTH=64)
(
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tvalid,
output reg i_tready,
//
output [WIDTH-1:0] o_tdata,
output o_tlast,
output reg o_tvalid,
input o_tready
);
reg [WIDTH:0] data_reg1, data_reg2;
reg [1:0] fifo_state;
localparam EMPTY = 0;
localparam HALF = 1;
localparam FULL = 2;
reg [1:0] extract_state;
localparam IDLE = 0;
localparam EXTRACT1 = 1;
localparam EXTRACT2 = 2;
localparam EXTRACT3 = 3;
always @(posedge clk)
if (reset | clear) begin
fifo_state <= EMPTY;
end else begin
case (fifo_state)
// Nothing in either register.
// Upstream can always push data to us.
// Downstream has nothing to take from us.
EMPTY: begin
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
// Embeded escpae code received.
extract_state <= EXTRACT1;
i_tready <= 1'b1;
o_tvalid <= 1'b0;
fifo_state <= EMPTY;
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
// Now work out if its a genuine embeded tlast or emulation.
i_tready <= 1'b1;
o_tvalid <= 1'b0;
fifo_state <= EMPTY;
if (i_tdata[31:0] == 'h1) begin
extract_state <= EXTRACT2;
end else begin
extract_state <= EXTRACT3;
end
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
// Extract tlast.
data_reg1 <= {1'b1,i_tdata};
i_tready <= 1'b1;
o_tvalid <= 1'b1;
fifo_state <= HALF;
extract_state <= IDLE;
end else if (i_tvalid) begin
// Get here both for normal data and for EXTRACT3 emulation data.
data_reg1 <= {1'b0,i_tdata};
fifo_state <= HALF;
extract_state <= IDLE;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end else begin
// Nothing to do.
fifo_state <= EMPTY;
i_tready <= 1'b1;
o_tvalid <= 1'b0;
end
end
// First Register Full.
// Upstream can always push data to us.
// Downstream can always read from us.
HALF: begin
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
// Embeded escpae code received.
extract_state <= EXTRACT1;
if (o_tready) begin
// If meanwhile we get read then go empty...
i_tready <= 1'b1;
o_tvalid <= 1'b0;
fifo_state <= EMPTY;
end else begin
// ...else stay half full.
fifo_state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
// Now work out if its a genuine embeded tlast or emulation.
if (i_tdata[31:0] == 'h1) begin
extract_state <= EXTRACT2;
end else begin
extract_state <= EXTRACT3;
end
if (o_tready) begin
// If meanwhile we get read then go empty...
i_tready <= 1'b1;
o_tvalid <= 1'b0;
fifo_state <= EMPTY;
end else begin
// ...else stay half full.
fifo_state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
// Extract tlast.
data_reg1 <= {1'b1,i_tdata};
extract_state <= IDLE;
if (o_tready) begin
// We get read and writen same cycle...
i_tready <= 1'b1;
o_tvalid <= 1'b1;
fifo_state <= HALF;
end else begin
// ...or we get written and go full.
data_reg2 <= data_reg1;
i_tready <= 1'b0;
o_tvalid <= 1'b1;
fifo_state <= FULL;
end
end else if (i_tvalid) begin
// Get here both for normal data and for EXTRACT3 emulation data.
data_reg1 <= {1'b0,i_tdata};
extract_state <= IDLE;
if (o_tready) begin
// We get read and writen same cycle...
fifo_state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end else begin
// ...or we get written and go full.
data_reg2 <= data_reg1;
i_tready <= 1'b0;
o_tvalid <= 1'b1;
fifo_state <= FULL;
end
end else if (o_tready) begin // if (i_tvalid)
// Only getting read this cycle so go empty
fifo_state <= EMPTY;
i_tready <= 1'b1;
o_tvalid <= 1'b0;
end else begin
// Absolutley nothing happens, everything stays the same.
fifo_state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
end // case: HALF
// Both Registers Full.
// Upstream can not push to us in this fifo_state.
// Downstream can always read from us.
FULL: begin
if (o_tready) begin
fifo_state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
else begin
fifo_state <= FULL;
i_tready <= 1'b0;
o_tvalid <= 1'b1;
end
end
endcase // case(fifo_state)
end // else: !if(reset | clear)
assign {o_tlast,o_tdata} = (fifo_state == FULL) ? data_reg2 : data_reg1;
endmodule // axi_fast_extract_tlast
+108
View File
@@ -0,0 +1,108 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Ultra fast critical path FIFO.
// Only 2 entrys but no combinatorial feed through paths
//
module axi_fast_fifo
#(parameter WIDTH=64)
(
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tvalid,
output reg i_tready,
//
output [WIDTH-1:0] o_tdata,
output reg o_tvalid,
input o_tready
);
reg [WIDTH-1:0] data_reg1, data_reg2;
reg [1:0] state;
localparam EMPTY = 0;
localparam HALF = 1;
localparam FULL = 2;
always @(posedge clk)
if (reset | clear) begin
state <= EMPTY;
data_reg1 <= 0;
data_reg2 <= 0;
o_tvalid <= 1'b0;
i_tready <= 1'b0;
end else begin
case (state)
// Nothing in either register.
// Upstream can always push data to us.
// Downstream has nothing to take from us.
EMPTY: begin
if (i_tvalid) begin
data_reg1 <= i_tdata;
state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end else begin
state <= EMPTY;
i_tready <= 1'b1;
o_tvalid <= 1'b0;
end
end
// First Register Full.
// Upstream can always push data to us.
// Downstream can always read from us.
HALF: begin
if (i_tvalid && o_tready) begin
data_reg1 <= i_tdata;
state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end else if (i_tvalid) begin
data_reg1 <= i_tdata;
data_reg2 <= data_reg1;
state <= FULL;
i_tready <= 1'b0;
o_tvalid <= 1'b1;
end else if (o_tready) begin
state <= EMPTY;
i_tready <= 1'b1;
o_tvalid <= 1'b0;
end else begin
state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
end // case: HALF
// Both Registers Full.
// Upstream can not push to us in this state.
// Downstream can always read from us.
FULL: begin
if (o_tready) begin
state <= HALF;
i_tready <= 1'b1;
o_tvalid <= 1'b1;
end
else begin
state <= FULL;
i_tready <= 1'b0;
o_tvalid <= 1'b1;
end
end
endcase // case(state)
end // else: !if(reset | clear)
assign o_tdata = (state == FULL) ? data_reg2 : data_reg1;
endmodule // axi_fast_fifo
+867
View File
@@ -0,0 +1,867 @@
//
// Copyright 2017 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0
//
// Module: axi_replay.v
// Description:
//
// This block implements the state machine and control logic for recording and
// playback of AXI-Stream data, using a DMA-accessible memory as a buffer.
module axi_replay #(
parameter DATA_WIDTH = 64,
parameter ADDR_WIDTH = 32, // Byte address width used by DMA master
parameter COUNT_WIDTH = 8 // Length of counters used to connect to the DMA
// master's read and write interfaces.
) (
input wire clk,
input wire rst, // Synchronous to clk
//---------------------------------------------------------------------------
// Settings Bus
//---------------------------------------------------------------------------
input wire set_stb,
input wire [ 7:0] set_addr,
input wire [31:0] set_data,
output reg [31:0] rb_data,
input wire [ 7:0] rb_addr,
//---------------------------------------------------------------------------
// AXI Stream Interface
//---------------------------------------------------------------------------
// Input
input wire [DATA_WIDTH-1:0] i_tdata,
input wire i_tvalid,
input wire i_tlast,
output wire i_tready,
// Output
output wire [DATA_WIDTH-1:0] o_tdata,
output wire o_tvalid,
output wire o_tlast,
input wire o_tready,
//---------------------------------------------------------------------------
// DMA Interface
//---------------------------------------------------------------------------
// Write interface
output reg [ ADDR_WIDTH-1:0] write_addr, // Byte address for start of write
// transaction (64-bit aligned).
output reg [COUNT_WIDTH-1:0] write_count, // Count of 64-bit words to write, minus 1.
output reg write_ctrl_valid,
input wire write_ctrl_ready,
output wire [ DATA_WIDTH-1:0] write_data,
output wire write_data_valid,
input wire write_data_ready,
// Read interface
output reg [ ADDR_WIDTH-1:0] read_addr, // Byte address for start of read
// transaction (64-bit aligned).
output reg [COUNT_WIDTH-1:0] read_count, // Count of 64-bit words to read, minus 1.
output reg read_ctrl_valid,
input wire read_ctrl_ready,
input wire [ DATA_WIDTH-1:0] read_data,
input wire read_data_valid,
output wire read_data_ready
);
//---------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------
// Size constants
localparam CMD_WIDTH = 32; // Command width
localparam LINES_WIDTH = 28; // Width of cmd_num_lines
localparam WORD_SIZE = DATA_WIDTH/8; // Size of DATA_WIDTH in bytes
// Register offsets
localparam [7:0] SR_REC_BASE_ADDR = 128;
localparam [7:0] SR_REC_BUFFER_SIZE = 129;
localparam [7:0] SR_REC_RESTART = 130;
localparam [7:0] SR_REC_FULLNESS = 131;
localparam [7:0] SR_PLAY_BASE_ADDR = 132;
localparam [7:0] SR_PLAY_BUFFER_SIZE = 133;
localparam [7:0] SR_RX_CTRL_COMMAND = 152; // Same offset as radio
localparam [7:0] SR_RX_CTRL_HALT = 155; // Same offset as radio
localparam [7:0] SR_RX_CTRL_MAXLEN = 156; // Same offset as radio
// Memory buffering parameters:
//
// Log base 2 of the depth of the input and output FIFOs to use. The FIFOs
// should be large enough to store more than a complete burst
// (MEM_BURST_SIZE). A size of 9 (512 64-bit words) is one 36-kbit BRAM.
localparam REC_FIFO_ADDR_WIDTH = 9; // Log2 of input/record FIFO size
localparam PLAY_FIFO_ADDR_WIDTH = 9; // Log2 of output/playback FIFO size
//
// Amount of data to buffer before writing to RAM. This should be a power of
// two so that it evenly divides the AXI_ALIGNMENT requirement. It also must
// not exceed 2**COUNT_WIDTH (the maximum count allowed by DMA master).
localparam MEM_BURST_SIZE = 2**COUNT_WIDTH; // Size in DATA_WIDTH-sized words
//
// AXI alignment requirement (4096 bytes) in DATA_WIDTH-bit words
localparam AXI_ALIGNMENT = 4096 / WORD_SIZE;
//
// Clock cycles to wait before writing something less than MEM_BURST_SIZE
// to memory.
localparam DATA_WAIT_TIMEOUT = 31;
//---------------------------------------------------------------------------
// Signals
//---------------------------------------------------------------------------
// Command wires
wire cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf;
wire [LINES_WIDTH-1:0] cmd_num_lines_cf;
// Settings registers signals
wire [ ADDR_WIDTH-1:0] rec_base_addr_sr; // Byte address
wire [ ADDR_WIDTH-1:0] rec_buffer_size_sr; // Size in bytes
wire [ ADDR_WIDTH-1:0] play_base_addr_sr; // Byte address
wire [ ADDR_WIDTH-1:0] play_buffer_size_sr; // Size in bytes
reg rec_restart;
reg rec_restart_clear;
wire [ CMD_WIDTH-1:0] command;
wire command_valid;
reg play_halt;
reg play_halt_clear;
wire [COUNT_WIDTH:0] play_max_len_sr;
// Command FIFO
wire cmd_fifo_valid;
reg cmd_fifo_ready;
// Record Data FIFO (Input)
wire [DATA_WIDTH-1:0] rec_fifo_o_tdata;
wire rec_fifo_o_tvalid;
wire rec_fifo_o_tready;
wire [ 15:0] rec_fifo_occupied;
// Playback Data FIFO (Output)
wire [DATA_WIDTH-1:0] play_fifo_i_tdata;
wire play_fifo_i_tvalid;
wire play_fifo_i_tready;
wire [ 15:0] play_fifo_space; // Free space in play_axi_fifo
// Buffer usage registers
reg [ADDR_WIDTH-1:0] rec_buffer_avail; // Amount of free buffer space in words
reg [ADDR_WIDTH-1:0] rec_buffer_used; // Amount of occupied buffer space in words
//---------------------------------------------------------------------------
// Registers
//---------------------------------------------------------------------------
// Record Base Address Register. Address is a byte address. This must be a
// multiple of 8 bytes.
setting_reg #(
.my_addr (SR_REC_BASE_ADDR),
.width (ADDR_WIDTH)
) sr_rec_base_addr (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (rec_base_addr_sr),
.changed ()
);
// Record Buffer Size Register. This indicates the portion of the RAM
// allocated to the record buffer, in bytes. This should be a multiple of 8
// bytes.
setting_reg #(
.my_addr (SR_REC_BUFFER_SIZE),
.width (ADDR_WIDTH)
) sr_rec_buffer_size (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (rec_buffer_size_sr),
.changed ()
);
// Playback Base Address Register. Address is a byte address. This must be a
// multiple of the 8 bytes.
setting_reg #(
.my_addr (SR_PLAY_BASE_ADDR),
.width (ADDR_WIDTH)
) sr_play_base_addr (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (play_base_addr_sr),
.changed ()
);
// Playback Buffer Size Register. This indicates the portion of the RAM
// allocated to the record buffer, in bytes. This should be a multiple of 8
// bytes.
setting_reg #(
.my_addr (SR_PLAY_BUFFER_SIZE),
.width (ADDR_WIDTH)
) sr_play_buffer_size (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (play_buffer_size_sr),
.changed ()
);
// Record Buffer Restart Register. Software must write to this register after
// updating the base address or buffer size. A write to this register means
// we need to stop any recording in progress and reset the record buffers
// according to the current buffer base address and size registers.
always @(posedge clk)
begin : sr_restart
if(rst) begin
rec_restart <= 1'b0;
end else begin
if(set_stb & (set_addr == SR_REC_RESTART)) begin
rec_restart <= 1'b1;
end else if (rec_restart_clear) begin
rec_restart <= 1'b0;
end
end
end
// Halt Register. A write to this register stops any replay operation as soon
// as the current DRAM transaction completes.
always @(posedge clk)
begin : sr_halt
if(rst) begin
play_halt <= 1'b0;
end else begin
if(set_stb & (set_addr == SR_RX_CTRL_HALT)) begin
play_halt <= 1'b1;
end else if (play_halt_clear) begin
play_halt <= 1'b0;
end
end
end
// Play Command Register
//
// This register mirrors the behavior of the RFNoC RX radio block. All
// commands are queued up in the replay command FIFO. The fields are as
// follows.
//
// send_imm [31] Send command immediately (don't use time).
//
// chain [30] When done with num_lines, immediately run next command.
//
// reload [29] When done with num_lines, rerun the same command if
// cmd_chain is set and no new command is available.
//
// stop [28] When done with num_lines, stop transferring if
// cmd_chain is set.
//
// num_lines [27:0] Number of samples to transfer to/from block.
//
setting_reg #(
.my_addr (SR_RX_CTRL_COMMAND),
.width (CMD_WIDTH)
) sr_command (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (command),
.changed (command_valid)
);
// Max Length Register. This register sets the number of words for the
// maximum packet size.
setting_reg #(
.my_addr (SR_RX_CTRL_MAXLEN),
.width (COUNT_WIDTH+1),
.at_reset({1'b1, {COUNT_WIDTH{1'b0}}})
) sr_max_len (
.clk (clk),
.rst (rst),
.strobe (set_stb),
.addr (set_addr),
.in (set_data),
.out (play_max_len_sr),
.changed ()
);
// Implement register read
always @(*) begin
case (rb_addr)
SR_REC_BASE_ADDR : rb_data = rec_base_addr_sr;
SR_REC_BUFFER_SIZE : rb_data = rec_buffer_size_sr;
SR_REC_FULLNESS : rb_data = rec_buffer_used * WORD_SIZE;
SR_PLAY_BASE_ADDR : rb_data = play_base_addr_sr;
SR_PLAY_BUFFER_SIZE : rb_data = play_buffer_size_sr;
SR_RX_CTRL_MAXLEN : rb_data = play_max_len_sr;
default : rb_data = 32'h0;
endcase
end
//---------------------------------------------------------------------------
// Playback Command FIFO
//---------------------------------------------------------------------------
//
// This block queues up commands for playback control.
//
//---------------------------------------------------------------------------
axi_fifo_short #(
.WIDTH (CMD_WIDTH)
) command_fifo (
.clk (clk),
.reset (rst),
.clear (play_halt_clear),
.i_tdata (command),
.i_tvalid (command_valid),
.i_tready (),
.o_tdata ({cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf, cmd_num_lines_cf}),
.o_tvalid (cmd_fifo_valid),
.o_tready (cmd_fifo_ready),
.occupied (),
.space ()
);
//---------------------------------------------------------------------------
// Record Input Data FIFO
//---------------------------------------------------------------------------
//
// This FIFO stores data to be recording into the RAM buffer.
//
//---------------------------------------------------------------------------
axi_fifo #(
.WIDTH (DATA_WIDTH),
.SIZE (REC_FIFO_ADDR_WIDTH)
) rec_axi_fifo (
.clk (clk),
.reset (rst),
.clear (1'b0),
//
.i_tdata (i_tdata),
.i_tvalid (i_tvalid),
.i_tready (i_tready),
//
.o_tdata (rec_fifo_o_tdata),
.o_tvalid (rec_fifo_o_tvalid),
.o_tready (rec_fifo_o_tready),
//
.space (),
.occupied (rec_fifo_occupied)
);
//---------------------------------------------------------------------------
// Record State Machine
//---------------------------------------------------------------------------
// FSM States
localparam REC_WAIT_FIFO = 0;
localparam REC_CHECK_ALIGN = 1;
localparam REC_DMA_REQ = 2;
localparam REC_WAIT_DMA_START = 3;
localparam REC_WAIT_DMA_COMMIT = 4;
// State Signals
reg [2:0] rec_state;
// Registers
reg [ADDR_WIDTH-1:0] rec_base_addr; // Last base address pulled from settings register
reg [ADDR_WIDTH-1:0] rec_buffer_size; // Last buffer size pulled from settings register
reg [ADDR_WIDTH-1:0] rec_addr; // Current offset into record buffer
reg [ADDR_WIDTH-1:0] rec_size; // Number of words to transfer next
reg [ADDR_WIDTH-1:0] rec_size_0; // Pipeline stage for computation of rec_size
reg signed [ADDR_WIDTH:0] rec_size_aligned; // rec_size reduced to not cross 4k boundary
// Timer to count how many cycles we've been waiting for new data
reg [$clog2(DATA_WAIT_TIMEOUT+1)-1:0] rec_wait_timer;
reg rec_wait_timeout;
always @(posedge clk) begin
if (rst) begin
rec_state <= REC_WAIT_FIFO;
rec_addr <= 0;
write_ctrl_valid <= 1'b0;
rec_buffer_avail <= 0;
rec_buffer_used <= 0;
rec_wait_timer <= 0;
rec_wait_timeout <= 0;
end else begin
// Default assignments
rec_restart_clear <= 1'b0;
// Update wait timer
if (i_tvalid || !rec_fifo_occupied) begin
// If a new word is presented to the input FIFO, or the FIFO is empty,
// then reset the timer.
rec_wait_timer <= 0;
rec_wait_timeout <= 1'b0;
end else if (rec_fifo_occupied) begin
// If no new word is written, but there's data in the FIFO, update the
// timer. Latch timeout condition when we reach out limit.
rec_wait_timer <= rec_wait_timer + 1;
if (rec_wait_timer == DATA_WAIT_TIMEOUT) begin
rec_wait_timeout <= 1'b1;
end
end
// Pre-calculate the aligned size
rec_size_aligned <= $signed(AXI_ALIGNMENT) - $signed(rec_addr & (AXI_ALIGNMENT-1));
//
// State logic
//
case (rec_state)
REC_WAIT_FIFO : begin
// Wait until there's enough data to initiate a transfer from the
// FIFO to the RAM.
// Check if a restart was requested on the record interface
if (rec_restart) begin
rec_restart_clear <= 1'b1;
// Latch the new register values. We don't want them to change
// while we're running.
rec_base_addr <= rec_base_addr_sr;
rec_buffer_size <= rec_buffer_size_sr / WORD_SIZE; // Store size in words
// Reset counters and address any time we update the buffer size or
// base address.
rec_buffer_avail <= rec_buffer_size_sr / WORD_SIZE; // Store size in words
rec_buffer_used <= 0;
rec_addr <= rec_base_addr_sr;
// Check if there's room left in the record RAM buffer
end else if (rec_buffer_used < rec_buffer_size) begin
// See if we can transfer a full burst
if (rec_fifo_occupied >= MEM_BURST_SIZE && rec_buffer_avail >= MEM_BURST_SIZE) begin
rec_size_0 <= MEM_BURST_SIZE;
rec_state <= REC_CHECK_ALIGN;
// Otherwise, if we've been waiting a long time, see if we can
// transfer less than a burst.
end else if (rec_fifo_occupied > 0 && rec_wait_timeout) begin
rec_size_0 <= (rec_fifo_occupied <= rec_buffer_avail) ?
rec_fifo_occupied : rec_buffer_avail;
rec_state <= REC_CHECK_ALIGN;
end
end
end
REC_CHECK_ALIGN : begin
// Check the address alignment, since AXI requires that an access not
// cross 4k boundaries (boo), and the axi_dma_master doesn't handle
// this automatically (boo again).
rec_size <= ($signed({1'b0,rec_size_0}) > rec_size_aligned) ?
rec_size_aligned : rec_size_0;
// DMA interface is ready, so transaction will begin
rec_state <= REC_DMA_REQ;
end
REC_DMA_REQ : begin
// The write count written to the DMA engine should be 1 less than
// the number of words you want to write (not the number of bytes).
write_count <= rec_size - 1;
// Create the physical RAM byte address by combining the address and
// base address.
write_addr <= rec_addr;
// Once the interface is ready, make the DMA request
if (write_ctrl_ready) begin
// Request the write transaction
write_ctrl_valid <= 1'b1;
rec_state <= REC_WAIT_DMA_START;
end
end
REC_WAIT_DMA_START : begin
// Wait until DMA interface deasserts ready, indicating it has
// started on the request.
write_ctrl_valid <= 1'b0;
if (!write_ctrl_ready) begin
rec_state <= REC_WAIT_DMA_COMMIT;
end
end
REC_WAIT_DMA_COMMIT : begin
// Wait for the DMA interface to reassert write_ctrl_ready, which
// signals that the DMA engine has received a response for the whole
// write transaction and (we assume) it has been committed to RAM.
// After this, we can update the write address and start the next
// transaction.
if (write_ctrl_ready) begin
rec_addr <= rec_addr + (rec_size * WORD_SIZE);
rec_buffer_used <= rec_buffer_used + rec_size;
rec_buffer_avail <= rec_buffer_avail - rec_size;
rec_state <= REC_WAIT_FIFO;
end
end
default : begin
rec_state <= REC_WAIT_FIFO;
end
endcase
end
end
// Connect output of record FIFO to input of DMA write interface
assign write_data = rec_fifo_o_tdata;
assign write_data_valid = rec_fifo_o_tvalid;
assign rec_fifo_o_tready = write_data_ready;
//---------------------------------------------------------------------------
// Playback State Machine
//---------------------------------------------------------------------------
// FSM States
localparam PLAY_IDLE = 0;
localparam PLAY_WAIT_DATA_READY = 1;
localparam PLAY_SIZE_CALC = 2;
localparam PLAY_DMA_REQ = 3;
localparam PLAY_WAIT_DMA_START = 4;
localparam PLAY_WAIT_DMA_COMMIT = 5;
localparam PLAY_DONE_CHECK = 6;
// State Signals
reg [2:0] play_state;
// Registers
reg [ADDR_WIDTH-1:0] play_base_addr; // Last base address pulled from settings register
reg [ADDR_WIDTH-1:0] play_buffer_size; // Last buffer size pulled from settings register
reg [ADDR_WIDTH-1:0] play_addr; // Current byte offset into record buffer
reg [ADDR_WIDTH-1:0] play_addr_0; // Pipeline stage for computing play_addr
reg [ADDR_WIDTH-1:0] play_addr_1; // Pipeline stage for computing play_addr
reg [ADDR_WIDTH-1:0] play_buffer_end; // Address of location after end of buffer
reg [ADDR_WIDTH-1:0] max_dma_size; // Maximum size of next transfer, in words
//
reg [LINES_WIDTH-1:0] cmd_num_lines; // Copy of cmd_num_lines from last command
reg [LINES_WIDTH-1:0] play_words_remaining; // Number of lines left to read for command
reg cmd_chain; // Copy of cmd_chain from last command
reg cmd_reload; // Copy of cmd_reload from last command
reg play_full_burst_avail; // True if we there's a full burst to read
reg play_buffer_avail_nonzero; // True if > 0
reg cmd_num_lines_cf_nonzero; // True if > 0
reg max_dma_size_ok; // True if it's OK to read max_dma_size
reg [ADDR_WIDTH-1:0] max_dma_size_m1; // max_dma_size - 1
reg [ADDR_WIDTH-1:0] play_words_remaining_m1; // play_words_remaining - 1
reg [ADDR_WIDTH-1:0] play_buffer_avail; // Number of words left to read in record buffer
reg [ADDR_WIDTH-1:0] play_buffer_avail_0; // Pipeline stage for computing play_buffer_avail
always @(posedge clk)
begin
if (rst) begin
play_state <= PLAY_IDLE;
cmd_fifo_ready <= 1'b0;
end else begin
// Calculate how many words are left to read from the record buffer
play_full_burst_avail <= (play_buffer_avail >= MEM_BURST_SIZE);
play_buffer_avail_nonzero <= (play_buffer_avail > 0);
cmd_num_lines_cf_nonzero <= (cmd_num_lines_cf > 0);
play_buffer_end <= play_base_addr_sr + play_buffer_size_sr;
// Default values
cmd_fifo_ready <= 1'b0;
read_ctrl_valid <= 1'b0;
play_halt_clear <= 1'b0;
//
// State logic
//
case (play_state)
PLAY_IDLE : begin
// Always start reading at the start of the record buffer
play_addr <= play_base_addr_sr;
// Save off command info, in case we need to repeat the command
cmd_num_lines <= cmd_num_lines_cf;
cmd_reload <= cmd_reload_cf;
cmd_chain <= cmd_chain_cf;
// Save the buffer info so it doesn't update during playback
play_base_addr <= play_base_addr_sr;
play_buffer_size <= play_buffer_size_sr;
play_buffer_avail <= play_buffer_size_sr / WORD_SIZE;
// Wait until we receive a command and we have enough data recorded
// to honor it.
if (cmd_fifo_valid && ~play_halt_clear) begin
// Load the number of word remaining to complete this command
play_words_remaining <= cmd_num_lines_cf;
// We don't support time yet, so we require send_imm to do
// anything. Also, we can't do anything until we have data recorded.
if (cmd_stop_cf) begin
// Do nothing, except clear command from the FIFO
cmd_fifo_ready <= 1'b1;
end else if (cmd_send_imm_cf
&& play_buffer_avail_nonzero
&& cmd_num_lines_cf_nonzero) begin
// Dequeue the command from the FIFO
cmd_fifo_ready <= 1'b1;
play_state <= PLAY_WAIT_DATA_READY;
end
end else if (play_halt) begin
// In case we get a HALT after a command has finished
play_halt_clear <= 1'b1;
end
end
PLAY_WAIT_DATA_READY : begin
// Save the maximum size we can read from RAM
max_dma_size <= play_full_burst_avail ? MEM_BURST_SIZE : play_buffer_avail;
// Check if we got a halt command while waiting
if (play_halt) begin
play_halt_clear <= 1'b1;
play_state <= PLAY_IDLE;
// Wait for output FIFO to empty sufficiently so we can read an
// entire burst at once. This may be more space than needed, but we
// won't know the exact size until the next state.
end else if (play_fifo_space >= MEM_BURST_SIZE) begin
play_state <= PLAY_SIZE_CALC;
end
end
PLAY_SIZE_CALC : begin
// Do some intermediate calculations to determine what the read_count
// should be.
play_words_remaining_m1 <= play_words_remaining-1;
max_dma_size_m1 <= max_dma_size-1;
max_dma_size_ok <= play_words_remaining >= max_dma_size;
play_state <= PLAY_DMA_REQ;
end
PLAY_DMA_REQ : begin
// Load the size of the next read into a register. We try to read the
// max amount available (up to the burst size) or however many words
// are needed to reach the end of the RAM buffer.
//
// The read count written to the DMA engine should be 1 less than the
// number of words you want to read (not the number of bytes).
read_count <= max_dma_size_ok ? max_dma_size_m1 : play_words_remaining_m1;
// Load the address to read. Note that we don't do an alignment check
// since we assume that multiples of MEM_BURST_SIZE meet the
// AXI_ALIGNMENT requirement.
read_addr <= play_addr;
// Request the read transaction as soon as DMA interface is ready
if (read_ctrl_ready) begin
read_ctrl_valid <= 1'b1;
play_state <= PLAY_WAIT_DMA_START;
end
end
PLAY_WAIT_DMA_START : begin
// Wait until DMA interface deasserts ready, indicating it has
// started on the request.
read_ctrl_valid <= 1'b0;
if (!read_ctrl_ready) begin
// Update values for next transaction
play_addr_0 <= play_addr + ({{(ADDR_WIDTH-COUNT_WIDTH){1'b0}}, read_count} + 1) * WORD_SIZE;
play_words_remaining <= play_words_remaining - ({1'b0, read_count} + 1);
play_buffer_avail_0 <= play_buffer_avail - ({1'b0, read_count} + 1);
play_state <= PLAY_WAIT_DMA_COMMIT;
end
end
PLAY_WAIT_DMA_COMMIT : begin
// Wait for the DMA interface to reassert read_ctrl_ready, which
// signals that the DMA engine has received a response for the whole
// read transaction.
if (read_ctrl_ready) begin
// Check if we need to wrap the address for the next transaction
if (play_addr_0 >= play_buffer_end) begin
play_addr_1 <= play_base_addr_sr;
play_buffer_avail <= play_buffer_size_sr / WORD_SIZE;
end else begin
play_addr_1 <= play_addr_0;
play_buffer_avail <= play_buffer_avail_0;
end
play_state <= PLAY_DONE_CHECK;
end
end
PLAY_DONE_CHECK : begin
play_addr <= play_addr_1;
// Check if we have more data to transfer for this command
if (play_words_remaining) begin
play_state <= PLAY_WAIT_DATA_READY;
// Check if we're chaining
end else if (cmd_chain) begin
// Check if there's a new command waiting
if (cmd_fifo_valid) begin
// Load the next command. Note that we don't reset the playback
// address when commands are chained together.
play_words_remaining <= cmd_num_lines_cf;
cmd_num_lines <= cmd_num_lines_cf;
cmd_reload <= cmd_reload_cf;
cmd_chain <= cmd_chain_cf;
// Dequeue the command from the FIFO
cmd_fifo_ready <= 1'b1;
// Stop if it's a stop command, otherwise restart
if (cmd_stop_cf) begin
play_state <= PLAY_IDLE;
end else begin
play_state <= PLAY_WAIT_DATA_READY;
end
// Check if we need to restart the previous command
end else if (cmd_reload) begin
play_words_remaining <= cmd_num_lines;
play_state <= PLAY_WAIT_DATA_READY;
end
// Nothing left to do
end else begin
play_state <= PLAY_IDLE;
end
end
endcase
end
end
// Connect output of DMA master to playback data FIFO
assign play_fifo_i_tdata = read_data;
assign play_fifo_i_tvalid = read_data_valid;
assign read_data_ready = play_fifo_i_tready;
//---------------------------------------------------------------------------
// TLAST Generation
//---------------------------------------------------------------------------
//
// This block monitors the signals to/from the DMA master and generates the
// TLAST signal. We assert TLAST at the end of every read transaction and
// after every play_max_len_sr words, so that no packets are longer than the
// length indicated by the max_len register.
//
// The timing of this block relies on the fact that read_ctrl_ready is not
// reasserted by the DMA master until after TLAST gets asserted.
//
//---------------------------------------------------------------------------
reg [COUNT_WIDTH-1:0] read_counter;
reg [COUNT_WIDTH-1:0] length_counter;
reg play_fifo_i_tlast;
always @(posedge clk)
begin
if (rst) begin
play_fifo_i_tlast <= 1'b0;
end else begin
// Check if we're requesting a read transaction
if (read_ctrl_valid && read_ctrl_ready) begin
// Initialize read_counter for new transaction
read_counter <= read_count;
length_counter <= play_max_len_sr;
// If read_count is 0, then the first word is also the last word
if (read_count == 0) begin
play_fifo_i_tlast <= 1'b1;
end
// Track the number of words read out by DMA master
end else if (read_data_valid && read_data_ready) begin
read_counter <= read_counter - 1;
length_counter <= length_counter - 1;
// Check if the word currently being output is the last word of a
// packet, which means we need to clear tlast.
if (play_fifo_i_tlast) begin
// But make sure that the next word isn't also the last of a DMA
// burst, for which we will need to keep tlast asserted.
if (read_counter != 1) begin
play_fifo_i_tlast <= 1'b0;
end
// Restart length counter
length_counter <= play_max_len_sr;
// Check if the next word to be output should be the last of a packet.
end else if (read_counter == 1 || length_counter == 2) begin
play_fifo_i_tlast <= 1'b1;
end
end
end
end
//---------------------------------------------------------------------------
// Playback Output Data FIFO
//---------------------------------------------------------------------------
//
// This FIFO buffers data that has been read out of RAM as part of a playback
// operation.
//
//---------------------------------------------------------------------------
axi_fifo #(
.WIDTH (DATA_WIDTH+1),
.SIZE (PLAY_FIFO_ADDR_WIDTH)
) play_axi_fifo (
.clk (clk),
.reset (rst),
.clear (1'b0),
//
.i_tdata ({play_fifo_i_tlast, play_fifo_i_tdata}),
.i_tvalid (play_fifo_i_tvalid),
.i_tready (play_fifo_i_tready),
//
.o_tdata ({o_tlast, o_tdata}),
.o_tvalid (o_tvalid),
.o_tready (o_tready),
//
.space (play_fifo_space),
.occupied ()
);
endmodule
+296
View File
@@ -0,0 +1,296 @@
//
// Copyright 2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Strips preamble, EOP, and CRC/num_words check
// <preamble> <packet> <EOP> [control_chksum,word_count,payload_chksum]
// <preamble> = 64'h9E6774129E677412
// <EOP> = 64'h2A1D632F2A1D632F
module axi_strip_preamble #(
parameter WIDTH=64,
parameter MAX_PKT_SIZE=512 //Set to 128 in sim to fill up buffers faster to help try and trigger more fail cases.
) (
input clk,
input reset,
input clear,
//
input [WIDTH-1:0] i_tdata,
input i_tvalid,
output i_tready,
//
output [WIDTH-1:0] o_tdata,
output o_tlast,
output o_tvalid,
input o_tready,
//
output pkt_dropped,
output crc_err,
output crit_error
);
function [0:0] cvita_get_has_time;
input [63:0] header;
cvita_get_has_time = header[61];
endfunction
//State machine info
reg [1:0] state, next_state;
localparam IDLE = 0;
localparam CHECK_HDR = 1;
localparam PASS = 2;
localparam CHECK_CRC = 3;
localparam PAYLOAD_WORDCOUNT_WIDTH = 16;
localparam PAYLOAD_CHKSUM_WIDTH = 32;
localparam CONTROL_CHKSUM_WIDTH = 16;
//Note that held_word is required when EOP is detected
//so that we can rewrite into memory the last word + last bit
reg [WIDTH-1:0] held_word;
reg [WIDTH-1:0] held_word_r;
always @(posedge clk) begin
if(i_tvalid && i_tready) begin
held_word <= i_tdata;
held_word_r <= held_word;
end
end
//Look for next word that specifies if frame has timestamp
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] cntrl_length = 16'd2;
always @(posedge clk) begin
if ((next_state == CHECK_HDR || state == CHECK_HDR) && i_tvalid)
cntrl_length <= cvita_get_has_time(i_tdata) ? 16'd2 : 16'd1;
end
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] word_count;
wire det_preamble = (i_tdata == 64'h9E6774129E677412);
wire det_eop = (i_tdata == 64'h2A1D632F2A1D632F);
wire [PAYLOAD_CHKSUM_WIDTH-1:0] payload_chksum;
wire [CONTROL_CHKSUM_WIDTH-1:0] control_chksum;
// Payload LFSR. Must hold LFSR once detected EOP so checksum does not keep updating after EOP
// Note the payload LFSR also includes the EOP in its checksum
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(PAYLOAD_CHKSUM_WIDTH)) payload_chksum_gen (
.clk(clk), .rst(word_count<=cntrl_length), .hold(~(i_tready && i_tvalid) || det_eop || state == CHECK_CRC),
.input_data(i_tdata), .crc_out(payload_chksum)
);
// Control LFSR. Varies in size based on whether the control information includes a timestamp
// Hold the LFSR once the control word(s) have been parsed
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(CONTROL_CHKSUM_WIDTH)) control_chksum_gen (
.clk(clk), .rst(word_count=='d0), .hold(~(i_tready && i_tvalid) || word_count>=cntrl_length),
.input_data(i_tdata), .crc_out(control_chksum)
);
//Good frame is when the word_count is correct and the control checksum passes.
//Allows passthrough of payloads with bit errors to reduce overall dropped frame rate
wire frame_good = (word_count == i_tdata[47:32]) && (control_chksum == i_tdata[63:48]) && state == CHECK_CRC;
//CRC error only increments when the state machine makes it to CHECK_CRC state
//It will not increment if a preamble or eop is detected outside of IDLE
wire payload_crc_check = (payload_chksum == i_tdata[31:0]) && state == CHECK_CRC;
assign crc_err = (~frame_good || ~payload_crc_check) && state == CHECK_CRC && i_tvalid;
//Increment word_count for payload and EOP
always @(posedge clk) begin
if (state == IDLE || pkt_dropped) begin
word_count <= 0;
end else if ((state == PASS || state == CHECK_HDR) && i_tready && i_tvalid) begin
word_count <= word_count+1'b1;
end
end
always @(posedge clk) begin
if (reset | clear) begin
state <= IDLE;
end else begin
state <= next_state;
end
end
//Only drop packet if preamble detected outside of idle or bad frame was detected during CRC check
assign pkt_dropped = ((state != IDLE) && det_preamble && i_tvalid) || ((state == CHECK_CRC) && ~frame_good && i_tvalid);
//When preamble is missing or has bit error, state machine stays in IDLE
//When EOP is missing or has bit error, either the next preamble is detected and resets logic
//or state machine exits on next EOP and fails CRC check.
//For cables with very high BER its possible for the write buffer to fill up which causes a critical error and resets everything
always @(*) begin
case(state)
IDLE: begin
if (det_preamble && i_tvalid) //Preamble detected so check to see if timestamp is part of header
next_state = CHECK_HDR;
else
next_state = IDLE;
end
//Check incoming word to see if frame will have timestamp
CHECK_HDR: begin
if(crit_error) begin //Critical error so reset SM
next_state = IDLE;
end else if(~det_preamble && i_tvalid && i_tready) begin //Found control word so go to normal pass state
next_state = PASS;
end else begin
next_state = CHECK_HDR;
end
end
//Note if early preamble is detected in PASS state everything is reset for the next frame
PASS: begin
if(crit_error) begin //Critical error so reset SM
next_state = IDLE;
end else if(det_preamble && i_tvalid) begin //Saw preamble so drop packet and start over
next_state = CHECK_HDR;
end else if(det_eop && i_tvalid && i_tready) begin //Saw EOP so check for crc on next word
next_state = CHECK_CRC;
end else begin
next_state = PASS;
end
end
//Check for crc and go to idle or go back to pass if another preamble is detected
CHECK_CRC: begin
if(crit_error) begin //Critical error so reset SM
next_state = IDLE;
end else if(det_preamble && i_tvalid) begin //Saw preamble so drop packet and start over
next_state = CHECK_HDR;
end else if(i_tvalid) begin //Got word which should've been the CRC
next_state = IDLE;
end else begin
next_state = CHECK_CRC;
end
end
default: begin
next_state = IDLE;
end
endcase
end
wire [WIDTH-1:0] buf_tdata;
wire buf_tlast, buf_tvalid, buf_tready, buf_empty;
reg buf_full = 1'b0;
wire [$clog2(MAX_PKT_SIZE)-1:0] valid_rd_addr;
reg buf_empty_r;
assign mem_tvalid = (state == PASS || state == CHECK_HDR) ? (i_tvalid && ~pkt_dropped) : 1'b0;
assign i_tready = (state == PASS || state == CHECK_HDR) ? buf_tready : 1'b1;
assign crit_error = buf_full && buf_empty; //This should never happen, if it does that indicates poor BER over Aurora or packet size too large
/////////////////////////////////////////////////
//Fifo to store incoming packets
//The write pntr rewinds whenever an error occurs
/////////////////////////////////////////////////
wire int_tready;
reg [$clog2(MAX_PKT_SIZE)-1:0] wr_addr, prev_wr_addr, rd_addr, old_rd_addr;
reg [$clog2(MAX_PKT_SIZE):0] in_pkt_cnt, out_pkt_cnt;
wire read = ~buf_empty && (int_tready || buf_empty_r); //Read from buffer if its no longer empty to prime output reg
wire almost_full = (wr_addr == valid_rd_addr-1'b1); //We need to look at the masked rd_addr in case its 1 ahead
assign buf_tready = ~buf_full;
wire write = mem_tvalid && buf_tready && ~det_eop;
//If frame was good we need to go back and rewrite the last word and set the last bit
wire [WIDTH:0] int_write_data = (frame_good) ? {1'b1,held_word_r} : {1'b0,i_tdata};
wire [$clog2(MAX_PKT_SIZE)-1:0] int_wr_addr = (frame_good) ? wr_addr-1 : wr_addr;
//BRAM inferred
wire [WIDTH:0] buf_data;
ram_2port #(.DWIDTH(WIDTH+1), .AWIDTH($clog2(MAX_PKT_SIZE))) pkt_buf
(.clka(clk), .ena(1'b1), .wea(1'b1), .addra(int_wr_addr),
.dia(int_write_data), .doa(),
.clkb(clk), .enb(read), .web(1'b0), .addrb(rd_addr), .dib(),
.dob(buf_data));
// Write logic
always @(posedge clk) begin
// Rewind logic
if(pkt_dropped || crit_error)
wr_addr <= prev_wr_addr;
else if(write)
wr_addr <= wr_addr + 1'b1;
if (almost_full) begin
if (write && ~read) begin
buf_full <= 1'b1;
end
end else begin
if (~write && read) begin
buf_full <= 1'b0;
end
end
if (frame_good) begin
in_pkt_cnt <= in_pkt_cnt + 1'b1;
prev_wr_addr <= wr_addr;
end
if (reset || clear) begin
wr_addr <= 0;
prev_wr_addr <= 0;
in_pkt_cnt <= 0;
end
if(reset || clear || crit_error) begin
buf_full <= 1'b0;
end
end
// Read logic. Hold data if pkt_count is equal
assign buf_empty = in_pkt_cnt == out_pkt_cnt;
reg last_word;
//Use current read addr only if read is enabled
assign valid_rd_addr = (read) ? rd_addr : old_rd_addr;
assign buf_tvalid = ~buf_empty_r && ~(last_word && buf_empty);
assign buf_tdata = buf_data[WIDTH-1:0];
assign buf_tlast = buf_data[WIDTH];
always @(posedge clk) begin
buf_empty_r <= buf_empty;
if (read) old_rd_addr <= rd_addr; //Keeps track of last valid rd_addr
//Last word has two possibilities
//If buffer empty then we need to rewind rd_addr and mask reading from buffer
//If buffer is not empty continue with rd_addr and continue reading from buffer
last_word <= buf_tvalid && int_tready && buf_tlast;
//Need to rewind rd_addr since it incremented one too far
//This means there will be one cycle where rd_addr is ahead of where it should be
//Other logic that uses rd_addr will have it masked for that cycle
if (last_word && buf_empty) rd_addr <= rd_addr - 1;
else if (read) rd_addr <= rd_addr + 1;
// Prevent output until we have a full packet
if (buf_tvalid && int_tready && buf_tlast) begin
out_pkt_cnt <= out_pkt_cnt + 1'b1;
end
if (reset || clear) begin
old_rd_addr <= 0;
rd_addr <= 0;
out_pkt_cnt <= 0;
end
end
assign o_tlast = buf_tlast;
assign o_tdata = buf_tdata;
assign o_tvalid = buf_tvalid;
assign int_tready = o_tready;
endmodule
+52
View File
@@ -0,0 +1,52 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Convert AXI Stream to a strobed interface.
// Note: Not especially useful if simply wanting to set
//
module axi_to_strobed #(
parameter WIDTH = 32,
parameter FIFO_SIZE = 1,
parameter MIN_RATE = 256
)(
input clk, input reset, input clear,
input [$clog2(MIN_RATE):0] out_rate, // Number of clock cycles between strobes
input ready,
output error, // Output strobe but no data
input [WIDTH-1:0] i_tdata, input i_tvalid, input i_tlast, output i_tready,
output out_stb, output out_last, output [WIDTH-1:0] out_data
);
reg strobe;
wire valid;
reg [$clog2(MIN_RATE):0] counter = 1;
always @(posedge clk) begin
if (reset | clear) begin
strobe <= 1'b0;
counter <= 1;
end else if (ready) begin
if (counter >= out_rate) begin
strobe <= 1'b1;
counter <= 1;
end else begin
strobe <= 1'b0;
counter <= counter + 1'b1;
end
end else begin
strobe <= 1'b0;
end
end
axi_fifo #(.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE)) axi_fifo (
.clk(clk), .reset(reset), .clear(clear),
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
.o_tdata({out_last,out_data}), .o_tvalid(valid), .o_tready(strobe),
.space(), .occupied());
assign out_stb = valid & strobe;
assign error = ~valid & strobe;
endmodule
+125
View File
@@ -0,0 +1,125 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_data_swap
// Description:
// A generic data swapper module for AXI-Stream. The contents of
// tdata are swapped based on the tswap signal. For each bit 'i'
// in tswap, adjacent words of width 2^i are swapped if tswap[i]
// is high. For example, if tswap[3] = 1, then each byte in tdata
// will be swapped with its adjacent neighbor. It is permissible
// for tswap to change for each transfer in an AXIS packet.
// Swapping can also be configured to be static (zero logic) by
// setting DYNAMIC = 0. To reduce area, certain swap stages can
// even be disabled. For example, if STAGES_EN[2:0] is set to 0
// then the lowest granularity for swaps will be a byte.
//
// Parameters:
// - DATA_W: Width of the tdata bus in bits
// - USER_W: Width of the tuser bus in bits
// - STAGES_EN: Which swap stages are enabled.
// - DYNAMIC: Dynamic swapping enabled (use tswap)
//
// Signals:
// - s_axis_*: The input AXI stream
// - m_axis_*: The output AXI stream
//
module axis_data_swap #(
parameter integer DATA_W = 256,
parameter integer USER_W = 1,
parameter [$clog2(DATA_W)-1:0] STAGES_EN = 'hFFFFFFFF, //@HACK: Vivado does not allow $clog2 in value of this expr
parameter [0:0] DYNAMIC = 1
)(
// Clock and Reset
input wire clk,
input wire rst,
// Input AXIS
input wire [DATA_W-1:0] s_axis_tdata,
input wire [$clog2(DATA_W)-2:0] s_axis_tswap,
input wire [USER_W-1:0] s_axis_tuser,
input wire s_axis_tlast,
input wire s_axis_tvalid,
output wire s_axis_tready,
// Output AXIS
output wire [DATA_W-1:0] m_axis_tdata,
output wire [USER_W-1:0] m_axis_tuser,
output wire m_axis_tlast,
output wire m_axis_tvalid,
input wire m_axis_tready
);
parameter SWAP_STAGES = $clog2(DATA_W);
parameter SWAP_W = $clog2(DATA_W)-1;
genvar s, w;
wire [DATA_W-1:0] stg_tdata [0:SWAP_STAGES], stg_tdata_swp[0:SWAP_STAGES], stg_tdata_res[0:SWAP_STAGES];
wire [SWAP_W-1:0] stg_tswap [0:SWAP_STAGES];
wire [USER_W-1:0] stg_tuser [0:SWAP_STAGES];
wire stg_tlast [0:SWAP_STAGES];
wire stg_tvalid[0:SWAP_STAGES];
wire stg_tready[0:SWAP_STAGES];
// Connect input and output to stage wires
generate
assign stg_tdata [0] = s_axis_tdata;
assign stg_tswap [0] = s_axis_tswap;
assign stg_tuser [0] = s_axis_tuser;
assign stg_tlast [0] = s_axis_tlast;
assign stg_tvalid[0] = s_axis_tvalid;
assign s_axis_tready = stg_tready[0];
assign m_axis_tdata = stg_tdata [SWAP_STAGES];
assign m_axis_tuser = stg_tuser [SWAP_STAGES];
assign m_axis_tlast = stg_tlast [SWAP_STAGES];
assign m_axis_tvalid = stg_tvalid[SWAP_STAGES];
assign stg_tready[SWAP_STAGES] = m_axis_tready;
endgenerate
// Instantiate AXIS flip-flops for each stage
generate
for (s = 0; s < SWAP_STAGES; s=s+1) begin
if (STAGES_EN[SWAP_STAGES-s-1]) begin
// Swap Logic
for (w = 0; w < (1<<s); w=w+1) begin
assign stg_tdata_swp[s][(w*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))] =
stg_tdata[s][(((1<<s)-w-1)*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))];
end
if (DYNAMIC) begin
// Honor tswap in DYNAMIC mode.
// Also add a flip_flop to break the long start-to-end critical path
assign stg_tdata_res[s] = (s > 0 && stg_tswap[s][SWAP_W-s]) ?
stg_tdata_swp[s] : stg_tdata[s];
// Flip-flop
axi_fifo_flop #(.WIDTH(DATA_W+SWAP_W+USER_W+1)) reg_i (
.clk(clk), .reset(rst), .clear(1'b0),
.i_tdata({stg_tlast[s], stg_tuser[s], stg_tswap[s], stg_tdata_res[s]}),
.i_tvalid(stg_tvalid[s]), .i_tready(stg_tready[s]),
.o_tdata({stg_tlast[s+1], stg_tuser[s+1], stg_tswap[s+1], stg_tdata[s+1]}),
.o_tvalid(stg_tvalid[s+1]), .o_tready(stg_tready[s+1]),
.occupied(), .space()
);
end else begin
// Static swapping logic
assign stg_tdata [s+1] = stg_tdata_swp[s];
assign stg_tswap [s+1] = stg_tswap [s];
assign stg_tuser [s+1] = stg_tuser [s];
assign stg_tlast [s+1] = stg_tlast [s];
assign stg_tvalid[s+1] = stg_tvalid [s];
assign stg_tready[s] = stg_tready [s+1];
end
end else begin
// Skip this stage
assign stg_tdata [s+1] = stg_tdata [s];
assign stg_tswap [s+1] = stg_tswap [s];
assign stg_tuser [s+1] = stg_tuser [s];
assign stg_tlast [s+1] = stg_tlast [s];
assign stg_tvalid[s+1] = stg_tvalid[s];
assign stg_tready[s] = stg_tready[s+1];
end
end
endgenerate
endmodule // axis_data_swap
+96
View File
@@ -0,0 +1,96 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_downsizer
// Description:
// An AXI-Stream width conversion module that narrows the input
// sample with by a factor of RATIO.
// NOTE: This module has end-to-end combanitorial paths. For a
// pipelined version, please use axis_width_conv
//
// Parameters:
// - OUT_DATA_W: The bitwidth of the output data bus. The width of the
// input data bus is OUT_DATA_W*RATIO
// - OUT_USER_W: The bitwidth of the output user bus. The width of the
// input user bus is OUT_USER_W*RATIO
// - RATIO: The downsizing ratio
//
// Signals:
// - s_axis_* : Input sample stream (AXI-Stream)
// - m_axis_* : Output sample stream (AXI-Stream)
module axis_downsizer #(
parameter OUT_DATA_W = 32,
parameter OUT_USER_W = 1,
parameter RATIO = 4
)(
// Clock, reset and settings
input wire clk, // Clock
input wire reset, // Reset
// Data In (AXI-Stream)
input wire [(OUT_DATA_W*RATIO)-1:0] s_axis_tdata, // Input stream tdata
input wire [(OUT_USER_W*RATIO)-1:0] s_axis_tuser, // Input stream tuser
input wire [RATIO-1:0] s_axis_tkeep, // Input stream tkeep
input wire s_axis_tlast, // Input stream tlast
input wire s_axis_tvalid, // Input stream tvalid
output wire s_axis_tready, // Input stream tready
// Data Out (AXI-Stream)
output wire [OUT_DATA_W-1:0] m_axis_tdata, // Output stream tdata
output wire [OUT_USER_W-1:0] m_axis_tuser, // Output stream tuser
output wire m_axis_tlast, // Output stream tlast
output wire m_axis_tvalid, // Output stream tvalid
input wire m_axis_tready // Output stream tready
);
genvar i;
generate if (RATIO != 1) begin
// Constants
localparam [$clog2(RATIO)-1:0] SEL_FIRST = 'd0;
localparam [$clog2(RATIO)-1:0] SEL_LAST = RATIO-1;
localparam [RATIO-1:0] KEEP_FIRST = {{(RATIO-1){1'b0}}, 1'b1};
localparam [RATIO-1:0] KEEP_ALL = {(RATIO){1'b1}};
// Keep a binary-coded and one-hot version of the current
// section of the input that is being processed.
reg [$clog2(RATIO)-1:0] select = SEL_FIRST;
reg [RATIO-1:0] keep = KEEP_FIRST;
// State machine to drive the select bits for the
// input selection MUX.
always @(posedge clk) begin
if (reset) begin
select <= SEL_FIRST;
keep <= KEEP_FIRST;
end else if (m_axis_tvalid & m_axis_tready) begin
select <= (select == SEL_LAST || m_axis_tlast) ? SEL_FIRST : (select + 'd1);
keep <= (keep == KEEP_ALL || m_axis_tlast) ? KEEP_FIRST : {keep[RATIO-2:0], 1'b1};
end
end
// The input selection MUX
wire [OUT_DATA_W-1:0] in_data[0:RATIO-1];
wire [OUT_USER_W-1:0] in_user[0:RATIO-1];
for (i = 0; i < RATIO; i=i+1) begin
assign in_data[i] = s_axis_tdata[i*OUT_DATA_W+:OUT_DATA_W];
assign in_user[i] = s_axis_tuser[i*OUT_USER_W+:OUT_USER_W];
end
assign m_axis_tdata = in_data[select];
assign m_axis_tuser = in_user[select];
assign m_axis_tlast = s_axis_tlast && (keep == s_axis_tkeep);
assign m_axis_tvalid = s_axis_tvalid;
assign s_axis_tready = m_axis_tvalid && m_axis_tready && ((keep == KEEP_ALL) || m_axis_tlast);
end else begin // if (RATIO != 1)
// Passthrough
assign m_axis_tdata = s_axis_tdata;
assign m_axis_tuser = s_axis_tuser;
assign m_axis_tlast = s_axis_tlast;
assign m_axis_tvalid = s_axis_tvalid;
assign s_axis_tready = m_axis_tready;
end endgenerate
endmodule // axis_downsizer
+148
View File
@@ -0,0 +1,148 @@
//
// Copyright 2018-2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_packet_flush
// Description:
// When this module is inserted in an AXI-Stream link, it allows
// the client to flip a bit to make the stream lossy. When enable=1
// all data coming through the input is dropped. This module can
// start and stop flushing at packet boundaries to ensure no partial
// packets are introduces into the stream. Set FLUSH_PARTIAL_PKTS = 1
// to disable that behavior. An optional timeout can be set to
// determine if flushing was done (without turning it off).
//
// Parameters:
// - WIDTH: The bitwidth of the AXI-Stream bus
// - TIMEOUT_W: Width of the timeout counter
// - FLUSH_PARTIAL_PKTS: Start flusing immediately even if a packet is in flight
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
//
// Signals:
// - s_axis_* : Input AXI-Stream
// - m_axis_* : Output AXI-Stream
// - enable : Enable flush mode
// - timeout : Flush timeout (# of cycles of inactivity until done)
// - flushing : The module is currently flushing
// - done : Finished flushing (but is still active)
module axis_packet_flush #(
parameter WIDTH = 64,
parameter TIMEOUT_W = 32,
parameter FLUSH_PARTIAL_PKTS = 0,
parameter PIPELINE = "NONE"
)(
// Clock and reset
input wire clk,
input wire reset,
// Control and status
input wire enable,
input wire [TIMEOUT_W-1:0] timeout,
output wire flushing,
output reg done = 1'b0,
// Input stream
input wire [WIDTH-1:0] s_axis_tdata,
input wire s_axis_tlast,
input wire s_axis_tvalid,
output wire s_axis_tready,
// Output stream
output wire [WIDTH-1:0] m_axis_tdata,
output wire m_axis_tlast,
output wire m_axis_tvalid,
input wire m_axis_tready
);
//----------------------------------------------
// Pipeline Logic
//----------------------------------------------
wire [WIDTH-1:0] i_pipe_tdata, o_pipe_tdata;
wire i_pipe_tlast, o_pipe_tlast;
wire i_pipe_tvalid, o_pipe_tvalid;
wire i_pipe_tready, o_pipe_tready;
generate
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH(WIDTH+1)) in_pipe_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({s_axis_tlast, s_axis_tdata}), .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
.o_tdata({i_pipe_tlast, i_pipe_tdata}), .o_tvalid(i_pipe_tvalid), .o_tready(i_pipe_tready),
.space(), .occupied()
);
end else begin
assign {i_pipe_tlast, i_pipe_tdata, i_pipe_tvalid} = {s_axis_tlast, s_axis_tdata, s_axis_tvalid};
assign s_axis_tready = i_pipe_tready;
end
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH(WIDTH+1)) out_pipe_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({o_pipe_tlast, o_pipe_tdata}), .i_tvalid(o_pipe_tvalid), .i_tready(o_pipe_tready),
.o_tdata({m_axis_tlast, m_axis_tdata}), .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
.space(), .occupied()
);
end else begin
assign {m_axis_tlast, m_axis_tdata, m_axis_tvalid} = {o_pipe_tlast, o_pipe_tdata, o_pipe_tvalid};
assign o_pipe_tready = m_axis_tready;
end
endgenerate
//----------------------------------------------
// Flushing Logic
//----------------------------------------------
// Shortcuts
wire xfer_stb = i_pipe_tvalid & i_pipe_tready;
wire pkt_stb = xfer_stb & i_pipe_tlast;
// Packet boundary detector
reg mid_pkt = 1'b0;
always @(posedge clk) begin
if (reset) begin
mid_pkt <= 1'b0;
end else if (xfer_stb) begin
mid_pkt <= ~pkt_stb;
end
end
// Flush startup state machine
reg active = 1'b0;
always @(posedge clk) begin
if (reset) begin
active <= 1'b0;
end else begin
if (enable & (pkt_stb | (~mid_pkt & ~xfer_stb))) begin
active <= 1'b1;
end else if (~enable) begin
active <= 1'b0;
end
end
end
assign flushing = (FLUSH_PARTIAL_PKTS == 0) ? active : enable;
// Flush done detector based on timeout
reg [TIMEOUT_W-1:0] cyc_to_go = {TIMEOUT_W{1'b1}};
wire done_tmp = (cyc_to_go == {TIMEOUT_W{1'b0}});
always @(posedge clk) begin
if (reset | ~enable) begin
cyc_to_go <= {TIMEOUT_W{1'b1}};
done <= 1'b0;
end else if (enable & ~active) begin
cyc_to_go <= timeout;
end else begin
if (~done_tmp) begin
cyc_to_go <= xfer_stb ? timeout : (cyc_to_go - 1'b1);
end
done <= done_tmp;
end
end
// When flushing, drop all input data and quiet output data
// When no flushing, pass data without interruption
assign o_pipe_tdata = i_pipe_tdata;
assign o_pipe_tlast = i_pipe_tlast;
assign o_pipe_tvalid = flushing ? 1'b0 : i_pipe_tvalid;
assign i_pipe_tready = flushing ? 1'b1 : o_pipe_tready;
endmodule
+209
View File
@@ -0,0 +1,209 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_shift_register
// Description:
// This module implements a chain of flip-flops in connected
// using AXI-Stream. It can be used in the following ways:
// * As a AXI-Stream shift register. The tready path is
// combinatorial from the output to the input so backpressure
// is immediate. The same behavior makes this module non-ideal
// to actually break timing critical paths.
// * An AXI-Stream wrapper module for a multi-cycle operation
// with clock-enables. This can most commonly be used with DSP
// operations like filters. Enable the sideband datapath to
// let the module handle handshaking while processing samples
// outside it.
//
// Parameters:
// - WIDTH: The bitwidth of a sample on the data bus.
// - NSPC: The number of parallel samples per cycle to process. The
// total width of the data bus will be WIDTH*NSPC.
// - LATENCY: Number of stages in the shift register
// - SIDEBAND_DATAPATH: If SIDEBAND_DATAPATH==1 then tdata is managed
// outside this module and imported from s_sideband_data.
// If SIDEBAND_DATAPATH=0, then tdata is managed internally and
// the sideband signals are unused.
// Useful when using this module to manage a DSP pipeline where the
// data could be changing in each stage.
// - GAPLESS: After the shift register has filled up, should gaps be
// allowed? If set to 1, then if s_axis_tvalid goes low then the
// pipeline will stall and all bits in stage_stb will immediately go low
// to ensure all stages in the shift register have valid data.
// NOTE: This GAPLESS=1 will not allow the final "LATENCY" samples
// to exit the shift register.
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
//
// Signals:
// - s_axis_* : Input sample stream (AXI-Stream)
// - m_axis_* : Output sample stream (AXI-Stream)
// - stage_stb : Transfer strobe for each stage
// - stage_eop : Transfer end-of-packet out. bit[i] = stage[i]
// - m_sideband_data : Sideband data out for external consumer
// - m_sideband_keep : Sideband keep signal out for external consumer
// - s_sideband_data : Sideband data in from external producer
module axis_shift_register #(
parameter WIDTH = 32,
parameter NSPC = 1,
parameter LATENCY = 3,
parameter SIDEBAND_DATAPATH = 0,
parameter GAPLESS = 0,
parameter PIPELINE = "NONE"
)(
// Clock, reset and settings
input wire clk, // Clock
input wire reset, // Reset
// Serial Data In (AXI-Stream)
input wire [(WIDTH*NSPC)-1:0] s_axis_tdata, // Input stream tdata
input wire [NSPC-1:0] s_axis_tkeep, // Input stream tkeep (used as a sample qualifier)
input wire s_axis_tlast, // Input stream tlast
input wire s_axis_tvalid, // Input stream tvalid
output wire s_axis_tready, // Input stream tready
// Serial Data Out (AXI-Stream)
output wire [(WIDTH*NSPC)-1:0] m_axis_tdata, // Output stream tdata
output wire [NSPC-1:0] m_axis_tkeep, // Output stream tkeep (used as a sample qualifier)
output wire m_axis_tlast, // Output stream tlast
output wire m_axis_tvalid, // Output stream tvalid
input wire m_axis_tready, // Output stream tready
// Signals for the sideband data path
output wire [LATENCY-1:0] stage_stb, // Transfer strobe out. bit[i] = stage[i]
output wire [LATENCY-1:0] stage_eop, // Transfer end-of-packet out. bit[i] = stage[i]
output wire [(WIDTH*NSPC)-1:0] m_sideband_data, // Sideband data out for external consumer
output wire [NSPC-1:0] m_sideband_keep, // Sideband keep signal out for external consumer
input wire [(WIDTH*NSPC)-1:0] s_sideband_data // Sideband data in from external producer
);
// Shift register width depends on whether the datapath is internal
localparam SHREG_WIDTH = SIDEBAND_DATAPATH[0] ? (NSPC + 1) : ((WIDTH*NSPC) + NSPC + 1);
localparam SHREG_TLAST_LOC = SHREG_WIDTH-1;
localparam SHREG_TKEEP_HI = SHREG_WIDTH-2;
localparam SHREG_TKEEP_LO = SHREG_WIDTH-NSPC-1;
//----------------------------------------------
// Pipeline Logic
// (fifo_flop2 is used because it breaks timing
// path going both ways: valid and ready)
//----------------------------------------------
wire [(WIDTH*NSPC)-1:0] i_tdata, o_tdata;
wire [NSPC-1:0] i_tkeep, o_tkeep;
wire i_tlast, o_tlast;
wire i_tvalid, o_tvalid;
wire i_tready, o_tready;
generate
// Input pipeline register if requested
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) in_pipe_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
.i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
.o_tdata({i_tlast, i_tkeep, i_tdata}), .o_tvalid(i_tvalid), .o_tready(i_tready),
.space(), .occupied()
);
end else begin
assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
assign i_tvalid = s_axis_tvalid;
assign s_axis_tready = i_tready;
end
// Output pipeline register if requested
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) out_pipe_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({o_tlast, o_tkeep, o_tdata}), .i_tvalid(o_tvalid), .i_tready(o_tready),
.o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
.o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
.space(), .occupied()
);
end else begin
assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
assign m_axis_tvalid = o_tvalid;
assign o_tready = m_axis_tready;
end
endgenerate
assign m_sideband_data = i_tdata;
assign m_sideband_keep = i_tkeep;
//----------------------------------------------
// Shift register stages
//----------------------------------------------
genvar i;
generate
if (GAPLESS == 0) begin
// Individual stage wires
wire [SHREG_WIDTH-1:0] stg_tdata [0:LATENCY];
wire stg_tvalid[0:LATENCY];
wire stg_tready[0:LATENCY];
// Shift register input
assign stg_tdata[0] = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
assign stg_tvalid[0] = i_tvalid;
assign i_tready = stg_tready[0];
// Shift register output
assign o_tlast = stg_tdata[LATENCY][SHREG_TLAST_LOC];
assign o_tkeep = stg_tdata[LATENCY][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
assign o_tdata = SIDEBAND_DATAPATH[0] ? s_sideband_data : stg_tdata[LATENCY][(WIDTH*NSPC)-1:0];
assign o_tvalid = stg_tvalid[LATENCY];
assign stg_tready[LATENCY] = o_tready;
for (i = 0; i < LATENCY; i=i+1) begin: stages
axi_fifo_flop #(.WIDTH(SHREG_WIDTH)) reg_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata(stg_tdata[i ]), .i_tvalid(stg_tvalid[i ]), .i_tready(stg_tready[i ]),
.o_tdata(stg_tdata[i+1]), .o_tvalid(stg_tvalid[i+1]), .o_tready(stg_tready[i+1]),
.occupied(), .space()
);
assign stage_stb[i] = stg_tvalid[i] & stg_tready[i];
assign stage_eop[i] = stage_stb[i] & stg_tdata[i][SHREG_TLAST_LOC];
end
end else begin // if (GAPLESS == 0)
wire [(WIDTH*NSPC)-1:0] o_tdata_fifo;
wire [NSPC-1:0] o_tkeep_fifo;
wire o_tlast_fifo, o_tvalid_fifo, o_tready_fifo;
// Shift register to hold valids
reg [LATENCY-1:0] stage_valid = {LATENCY{1'b0}};
// Shift register to hold data/last
reg [SHREG_WIDTH-1:0] stage_shreg[0:LATENCY-1];
wire [SHREG_WIDTH-1:0] shreg_input = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
wire shreg_ce = i_tready & i_tvalid;
assign i_tready = o_tready_fifo;
assign o_tvalid_fifo = stage_valid[LATENCY-1] & shreg_ce;
assign o_tlast_fifo = stage_shreg[LATENCY-1][SHREG_TLAST_LOC];
assign o_tkeep_fifo = stage_shreg[LATENCY-1][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
assign o_tdata_fifo = SIDEBAND_DATAPATH[0] ? s_sideband_data : stage_shreg[LATENCY-1][(WIDTH*NSPC)-1:0];
for (i = 0; i < LATENCY; i=i+1) begin
// Initialize shift register
initial begin
stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
end
// Shift register logic
always @(posedge clk) begin
if (reset) begin
stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
stage_valid[i] <= 1'b0;
end else if (shreg_ce) begin
stage_shreg[i] <= (i == 0) ? shreg_input : stage_shreg[i-1];
stage_valid[i] <= (i == 0) ? 1'b1 : stage_valid[i-1];
end
end
// Outputs
assign stage_stb[i] = ((i == 0) ? 1'b1 : stage_valid[i-1]) & shreg_ce;
assign stage_eop[i] = stage_stb[i] & ((i == 0) ? i_tlast : stage_shreg[i-1][SHREG_TLAST_LOC]);
end
// The "gapless" logic violates AXI-Stream by having an o_tready -> o_tvalid dependency,
// so we add a FIFO downstream to prevent deadlocks.
axi_fifo #(.WIDTH((WIDTH*NSPC) + NSPC + 1), .SIZE($clog2(LATENCY))) out_fifo_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({o_tlast_fifo, o_tkeep_fifo, o_tdata_fifo}), .i_tvalid(o_tvalid_fifo), .i_tready(o_tready_fifo),
.o_tdata({o_tlast, o_tkeep, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
.space(), .occupied()
);
end
endgenerate
endmodule // axis_shift_register
+104
View File
@@ -0,0 +1,104 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_upsizer
// Description:
// An AXI-Stream width conversion module that widens the input
// sample with by a factor of RATIO.
// NOTE: This module has end-to-end combanitorial paths. For a
// pipelined version, please use axis_width_conv
//
// Parameters:
// - IN_DATA_W: The bitwidth of the input data bus. The width of the
// output data bus is IN_DATA_W*RATIO
// - IN_USER_W: The bitwidth of the input user bus. The width of the
// output user bus is IN_USER_W*RATIO
// - RATIO: The upsizing ratio
//
// Signals:
// - s_axis_* : Input sample stream (AXI-Stream)
// - m_axis_* : Output sample stream (AXI-Stream)
module axis_upsizer #(
parameter IN_DATA_W = 32,
parameter IN_USER_W = 1,
parameter RATIO = 4
)(
// Clock, reset and settings
input wire clk, // Clock
input wire reset, // Reset
// Data In (AXI-Stream)
input wire [IN_DATA_W-1:0] s_axis_tdata, // Input stream tdata
input wire [IN_USER_W-1:0] s_axis_tuser, // Input stream tuser
input wire s_axis_tlast, // Input stream tlast
input wire s_axis_tvalid, // Input stream tvalid
output wire s_axis_tready, // Input stream tready
// Data Out (AXI-Stream)
output wire [(IN_DATA_W*RATIO)-1:0] m_axis_tdata, // Output stream tdata
output wire [(IN_USER_W*RATIO)-1:0] m_axis_tuser, // Output stream tuser
output wire [RATIO-1:0] m_axis_tkeep, // Output stream tkeep
output wire m_axis_tlast, // Output stream tlast
output wire m_axis_tvalid, // Output stream tvalid
input wire m_axis_tready // Output stream tready
);
genvar i;
generate if (RATIO != 1) begin
// Constants
localparam [$clog2(RATIO)-1:0] SEL_FIRST = 'd0;
localparam [$clog2(RATIO)-1:0] SEL_LAST = RATIO-1;
localparam [RATIO-1:0] KEEP_FIRST = {{(RATIO-1){1'b0}}, 1'b1};
localparam [RATIO-1:0] KEEP_ALL = {(RATIO){1'b1}};
// Keep a binary-coded and one-hot version of the current
// section of the output that is being processed.
reg [$clog2(RATIO)-1:0] select = SEL_FIRST;
reg [RATIO-1:0] keep = KEEP_FIRST;
// Cached data. Incomplete output word.
reg [IN_DATA_W-1:0] cached_data[0:RATIO-2];
reg [IN_USER_W-1:0] cached_user[0:RATIO-2];
// State machine to drive the select bits for the
// output DEMUX.
always @(posedge clk) begin
if (reset) begin
select <= SEL_FIRST;
keep <= KEEP_FIRST;
end else if (s_axis_tvalid & s_axis_tready) begin
select <= (select == SEL_LAST || s_axis_tlast) ? SEL_FIRST : (select + 'd1);
keep <= (keep == KEEP_ALL || s_axis_tlast) ? KEEP_FIRST : {keep[RATIO-2:0], 1'b1};
cached_data[select] <= s_axis_tdata;
cached_user[select] <= s_axis_tuser;
end
end
// The output DEMUX
for (i = 0; i < RATIO; i=i+1) begin
if (i == SEL_LAST) begin
assign m_axis_tdata[(i*IN_DATA_W)+:IN_DATA_W] = s_axis_tdata;
assign m_axis_tuser[(i*IN_USER_W)+:IN_USER_W] = s_axis_tuser;
end else begin
assign m_axis_tdata[(i*IN_DATA_W)+:IN_DATA_W] = keep[i+1] ? cached_data[i] : s_axis_tdata;
assign m_axis_tuser[(i*IN_USER_W)+:IN_USER_W] = keep[i+1] ? cached_user[i] : s_axis_tuser;
end
end
assign m_axis_tkeep = keep;
assign m_axis_tlast = s_axis_tlast;
assign m_axis_tvalid = s_axis_tvalid & ((keep == KEEP_ALL) | s_axis_tlast);
assign s_axis_tready = m_axis_tvalid ? m_axis_tready : s_axis_tvalid;
end else begin // if (RATIO != 1)
// Passthrough
assign m_axis_tdata = s_axis_tdata;
assign m_axis_tuser = s_axis_tuser;
assign m_axis_tkeep = 1'b1;
assign m_axis_tlast = s_axis_tlast;
assign m_axis_tvalid = s_axis_tvalid;
assign s_axis_tready = m_axis_tready;
end endgenerate
endmodule // axis_upsizer
+232
View File
@@ -0,0 +1,232 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_width_conv
// Description:
// An AXI-Stream width conversion module that can convert from
// an arbitrary input width to an arbitrary output width. The
// module also supports an optional clock crossing. Data bits
// are grouped into words which will be rearranged by this module.
// The contents of a word are not rearranged.
// Example (WORD_W=4, IN_WORDS=4, OUT_WORDS=6):
// Input : 3_1_2_0, x_6_5_4 (comma-delimited packets)
// Output : 5_4_3_2_1_0, x_x_x_x_x_6 (comma-delimited packets)
// NOTE: The use of tkeep in this module is a slight deviation from
// the AXI standard where the bits are "byte qualifiers". In
// this module, tkeep is a "word qualifier" where the width
// of a word can be arbitrary. If WORD_W = 8, the behavior
// of this module is identical to an AXI width converter.
//
// Parameters:
// - WORD_W: Bitwidth of a word
// - IN_WORDS: Number of words in the input stream
// - OUT_WORDS: Number of words in the output stream
// - SYNC_CLKS: Are s_axis_aclk and m_axis_aclk synchronous to each other?
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
//
// Signals:
// - s_axis_* : Input sample stream (AXI-Stream)
// - m_axis_* : Output sample stream (AXI-Stream)
module axis_width_conv #(
parameter WORD_W = 8,
parameter IN_WORDS = 4,
parameter OUT_WORDS = 6,
parameter SYNC_CLKS = 0,
parameter PIPELINE = "NONE"
)(
// Data In (AXI-Stream)
input wire s_axis_aclk, // Input stream Clock
input wire s_axis_rst, // Input stream Reset
input wire [(IN_WORDS*WORD_W)-1:0] s_axis_tdata, // Input stream tdata
input wire [IN_WORDS-1:0] s_axis_tkeep, // Input stream tkeep
input wire s_axis_tlast, // Input stream tlast
input wire s_axis_tvalid, // Input stream tvalid
output wire s_axis_tready, // Input stream tready
// Data Out (AXI-Stream)
input wire m_axis_aclk, // Output stream Clock
input wire m_axis_rst, // Output stream Reset
output wire [(OUT_WORDS*WORD_W)-1:0] m_axis_tdata, // Output stream tdata
output wire [OUT_WORDS-1:0] m_axis_tkeep, // Output stream tkeep
output wire m_axis_tlast, // Output stream tlast
output wire m_axis_tvalid, // Output stream tvalid
input wire m_axis_tready // Output stream tready
);
//----------------------------------------------
// Pipeline Logic
//----------------------------------------------
// Add optional input and output pipeline stages
wire [(IN_WORDS*WORD_W)-1:0] i_tdata;
wire [IN_WORDS-1:0] i_tkeep;
wire i_tlast, i_tvalid, i_tready;
wire [(OUT_WORDS*WORD_W)-1:0] o_tdata;
wire [OUT_WORDS-1:0] o_tkeep;
wire o_tlast, o_tvalid, o_tready;
generate
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((IN_WORDS*(WORD_W+1))+1)) in_pipe_i (
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
.i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
.i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
.o_tdata({i_tlast, i_tkeep, i_tdata}),
.o_tvalid(i_tvalid), .o_tready(i_tready),
.space(), .occupied()
);
end else begin
assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
assign i_tvalid = s_axis_tvalid;
assign s_axis_tready = i_tready;
end
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH((OUT_WORDS*(WORD_W+1))+1)) out_pipe_i (
.clk(m_axis_aclk), .reset(m_axis_rst), .clear(1'b0),
.i_tdata({o_tlast, o_tkeep, o_tdata}),
.i_tvalid(o_tvalid), .i_tready(o_tready),
.o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
.o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
.space(), .occupied()
);
end else begin
assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
assign m_axis_tvalid = o_tvalid;
assign o_tready = m_axis_tready;
end
endgenerate
//----------------------------------------------
// Intermediate Data Bus
//----------------------------------------------
// To perform an M to N width conversion, we first
// convert from M to LCM(M, N), then to N
// Function to compute the least common multiple
// of two numbers (parameters or localparams only)
function integer lcm;
input integer a;
input integer b;
integer x, y, swap;
reg done;
begin
done = 1'b0;
x = a;
y = b;
while (!done) begin
if (x < y) begin
swap = x;
x = y;
y = swap;
end else if (y != 0) begin
x = x - y;
end else begin
done = 1'b1;
end
end
// x is the greatest common divisor
// LCM = (a*b)/GCD
lcm = (a*b)/x;
end
endfunction
// Intermediate bus parameters
localparam integer INT_KEEP_W = lcm(IN_WORDS, OUT_WORDS);
localparam integer INT_DATA_W = INT_KEEP_W * WORD_W;
localparam integer UPSIZE_RATIO = INT_KEEP_W / IN_WORDS;
localparam integer DOWNSIZE_RATIO = INT_KEEP_W / OUT_WORDS;
wire [INT_DATA_W-1:0] fifo_i_tdata, fifo_o_tdata;
wire [INT_KEEP_W-1:0] fifo_i_tkeep, fifo_o_tkeep;
wire fifo_i_tlast, fifo_i_tvalid, fifo_i_tready;
wire fifo_o_tlast, fifo_o_tvalid, fifo_o_tready;
// Skip the intermediate FIFO if
// - The input and output clocks are the same
// - The upsizer is effectively a passthrough and input registering is requested
// - The downsizer is effectively a passthrough and output registering is requested
localparam [0:0] SKIP_FIFO = (SYNC_CLKS == 1) && (
((PIPELINE == "IN" || PIPELINE == "INOUT") && (UPSIZE_RATIO == 1)) ||
((PIPELINE == "OUT" || PIPELINE == "INOUT") && (DOWNSIZE_RATIO == 1))
);
localparam FIFO_SIZE = 1;
//----------------------------------------------
// In => Upsizer => FIFO => Downsizer => Out
//----------------------------------------------
wire [INT_KEEP_W-1:0] up_keep_flat;
wire [UPSIZE_RATIO-1:0] up_keep_keep;
wire [DOWNSIZE_RATIO-1:0] down_keep_keep;
axis_upsizer #(
.IN_DATA_W(IN_WORDS*WORD_W), .IN_USER_W(IN_WORDS),
.RATIO(UPSIZE_RATIO)
) upsizer_i (
.clk(s_axis_aclk), .reset(s_axis_rst),
.s_axis_tdata(i_tdata), .s_axis_tuser(i_tkeep),
.s_axis_tlast(i_tlast), .s_axis_tvalid(i_tvalid), .s_axis_tready(i_tready),
.m_axis_tdata(fifo_i_tdata), .m_axis_tuser(up_keep_flat), .m_axis_tkeep(up_keep_keep),
.m_axis_tlast(fifo_i_tlast), .m_axis_tvalid(fifo_i_tvalid), .m_axis_tready(fifo_i_tready)
);
// tkeep unmasking logic after upsizer
genvar i;
generate for (i = 0; i < INT_KEEP_W; i = i + 1) begin
// tkeep is assumed to be valid only when tlast is asserted
// otherwise it is 1
assign fifo_i_tkeep[i] = ~fifo_i_tlast |
(up_keep_keep[i/IN_WORDS] ? up_keep_flat[i] : 1'b0);
end endgenerate
generate
if (SKIP_FIFO) begin
assign fifo_o_tdata = fifo_i_tdata;
assign fifo_o_tkeep = fifo_i_tkeep;
assign fifo_o_tlast = fifo_i_tlast;
assign fifo_o_tvalid = fifo_i_tvalid;
assign fifo_i_tready = fifo_o_tready;
end else begin
if (SYNC_CLKS) begin
axi_fifo #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready),
.space(), .occupied()
);
end else begin
axi_fifo_2clk #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
.reset(s_axis_rst),
.i_aclk(s_axis_aclk),
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
.o_aclk(m_axis_aclk),
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready)
);
end
end
endgenerate
// tkeep masking logic after downsizer
generate for (i = 0; i < DOWNSIZE_RATIO; i = i + 1) begin
assign down_keep_keep[i] = |fifo_o_tkeep[i*OUT_WORDS+:OUT_WORDS];
end endgenerate
axis_downsizer #(
.OUT_DATA_W(OUT_WORDS*WORD_W), .OUT_USER_W(OUT_WORDS),
.RATIO(DOWNSIZE_RATIO)
) downsizer_i (
.clk(m_axis_aclk), .reset(m_axis_rst),
.s_axis_tdata(fifo_o_tdata), .s_axis_tuser(fifo_o_tkeep), .s_axis_tkeep(down_keep_keep),
.s_axis_tlast(fifo_o_tlast), .s_axis_tvalid(fifo_o_tvalid), .s_axis_tready(fifo_o_tready),
.m_axis_tdata(o_tdata), .m_axis_tuser(o_tkeep),
.m_axis_tlast(o_tlast), .m_axis_tvalid(o_tvalid), .m_axis_tready(o_tready)
);
endmodule // axis_width_conv
+57
View File
@@ -0,0 +1,57 @@
//
// Copyright 2017 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Generates an LFSR based on a given seed value
// Note that not all length LFSRs are supported in the file
// For xnor LSFR equations please refer to following link:
// https://www.xilinx.com/support/documentation/application_notes/xapp210.pdf
// All indexing will be from 1 to match indexing used in table from app note above
module crc_xnor #(
parameter INPUT_WIDTH=64,
parameter OUTPUT_WIDTH=8
) (
input clk,
input [INPUT_WIDTH:1] input_data,
input rst,
input hold,
output [OUTPUT_WIDTH:1] crc_out
);
wire [INPUT_WIDTH:1] current_lfsr;
reg [INPUT_WIDTH:1] current_lfsr_r;
// LFSR based on table given by Xilinx
generate if (INPUT_WIDTH == 64) begin
assign current_lfsr[1] = current_lfsr_r[64] ^ current_lfsr_r[63] ^ current_lfsr_r[61] ^ current_lfsr_r[60];
assign current_lfsr[INPUT_WIDTH:2] = current_lfsr_r[INPUT_WIDTH-1:1];
end else begin
fake_error_thrower invalid_width_parameter();
end endgenerate
always @(posedge clk) begin
if (rst) begin
current_lfsr_r <= input_data;
end else if(~hold) begin
current_lfsr_r <= current_lfsr ^ input_data;
end
end
// Sum reduce based on output width
generate if(INPUT_WIDTH == 64 && OUTPUT_WIDTH == 16) begin
assign crc_out = current_lfsr_r[INPUT_WIDTH:INPUT_WIDTH/4*3+1]+current_lfsr_r[INPUT_WIDTH/4*3:INPUT_WIDTH/4*2+1]+
current_lfsr_r[INPUT_WIDTH/4*2:INPUT_WIDTH/4+1]+current_lfsr_r[INPUT_WIDTH/4:1];
end else if(INPUT_WIDTH == 64 && OUTPUT_WIDTH == 32) begin
assign crc_out = current_lfsr_r[INPUT_WIDTH:INPUT_WIDTH/2+1]+current_lfsr_r[INPUT_WIDTH/2:1];
end else begin
fake_error_thrower invalid_width_parameter();
end endgenerate
endmodule
+22
View File
@@ -0,0 +1,22 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module strobed_to_axi #(
parameter WIDTH = 32,
parameter FIFO_SIZE = 1
)(
input clk, input reset, input clear,
input in_stb, input [WIDTH-1:0] in_data, input in_last,
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
);
axi_fifo #(.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE)) axi_fifo (
.clk(clk), .reset(reset), .clear(clear),
.i_tdata({in_last,in_data}), .i_tvalid(in_stb), .i_tready(),
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
.space(), .occupied());
endmodule
+61
View File
@@ -0,0 +1,61 @@
#
# Copyright 2013 Ettus Research LLC
# Copyright 2017 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
##################################################
# Control Lib Sources
##################################################
CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
ad5662_auto_spi.v \
arb_qualify_master.v \
axi_crossbar.v \
axi_crossbar_regport.v \
axi_fifo_header.v \
axi_forwarding_cam.v \
axi_setting_reg.v \
axi_slave_mux.v \
axi_test_vfifo.v \
bin2gray.v \
binary_encoder.v \
db_control.v \
fe_control.v \
filter_bad_sid.v \
gpio_atr_io.v \
gpio_atr.v \
gray2bin.v \
por_gen.v \
priority_encoder_one_hot.v \
priority_encoder.v \
ram_2port_impl.vh \
ram_2port.v \
reset_sync.v \
s7_icap_wb.v \
serial_to_settings.v \
setting_reg.v \
settings_bus_mux.v \
settings_bus_timed_2clk.v \
simple_i2c_core.v \
simple_spi_core.v \
synchronizer_impl.v \
synchronizer.v \
pulse_synchronizer.v \
user_settings.v \
axil_regport_master.v \
axil_to_ni_regport.v \
regport_resp_mux.v \
regport_to_xbar_settingsbus.v \
regport_to_settingsbus.v \
pulse_stretch.v \
pulse_stretch_min.v \
mdio_master.v \
map/cam_priority_encoder.v \
map/cam_bram.v \
map/cam_srl.v \
map/cam.v \
map/kv_map.v \
map/axis_muxed_kv_map.v \
axil_ctrlport_master.v\
))
+99
View File
@@ -0,0 +1,99 @@
//
// Copyright 2015 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// The AD5662 DAC serial interface uses 24-bit transfers to encode 16-bits
// of actual data, two bits for power-down mode, and six pad bits. This
// module stores a copy of the last-programmed value, and will generate a
// serial stream if ever the input word (dat) changes. It will ignore
// changes to (dat) while it is busy with a serial update.
//
module ad5662_auto_spi
(
input clk,
input [15:0] dat,
output reg sclk,
output reg mosi,
output reg sync_n
);
// initialize ldat to 0, thus forcing
// a reload on init.
reg [15:0] ldat = 16'd0;
wire upd = (dat != ldat); // new data present, need to update hw
reg [23:0] shft=24'b0;
wire [23:0] nxt_shft;
// clock cycle counter to throttle generated spi cycles
// allowing one spi clock cycle every 16 cycles of clk, with clk at 200 MHz
// gives a spi clock rate of 12 MHz. This can be made more sophisticated
// or parameterized, if more flexibility in clk is needed, of course.
reg [3:0] ccnt=4'b0;
wire [3:0] nxt_ccnt = ccnt + 1'b1;
wire half = ccnt==4'b1000;
wire full = ccnt==4'b1111;
reg sena, hena;
wire cena;
always @(posedge clk) if (cena) ccnt <= nxt_ccnt;
always @(posedge clk) sena <= full; // state updates and rising sclk
always @(posedge clk) hena <= half; // for falling sclk
// transfer state counter
reg [4:0] scnt = 5'b0;
reg [4:0] nxt_scnt;
always @(posedge clk) begin
if (sena) begin
scnt <= nxt_scnt;
shft <= nxt_shft;
mosi <= shft[23];
end
end
// 32 possible states - more than enough to shift-out 24 bits and manage
// the sync_n line
// particular scnt values of interest
localparam READY=5'b00000; // waiting for new data
localparam DCAPT=5'b00001; // new data transfers into ldat
localparam SYNCL=5'b00010; // assert sync_n low
localparam SYNCH=5'b11011; // return sync_n high
assign cena = upd | scnt != READY;
always @(scnt or upd)
begin
case (scnt)
READY:
nxt_scnt = upd ? DCAPT : READY;
SYNCH:
nxt_scnt = READY;
default:
nxt_scnt = scnt + 1'b1;
endcase
end
// note: defining the power-down mode bits to 00 for "normal operation"
assign nxt_shft = (scnt == SYNCL) ? { 8'b000000_00, ldat } : { shft[22:0], 1'b0 };
// Update ldat when dat has changed, but only if READY.
// Changes to dat arriving faster than can be kept up with here are ignored
// until the cycle-in-progress is completed.
wire ldat_ena = sena & (scnt == DCAPT);
always @(posedge clk) begin
if (ldat_ena) ldat <= dat;
end
// keep the sync_n line low when idle to minimize power consumption
// it gets brought high just before beginning each transaction
wire nxt_sync_n = (scnt==SYNCL) | (scnt==SYNCH);
always @(posedge clk) if (sena) sync_n <= nxt_sync_n;
reg sclk_go;
always @(posedge clk) sclk_go <= (scnt > SYNCL);
wire nxt_sclk = ~sclk_go ? 1'b1 : ~sclk;
always @(posedge clk) if (sena | hena) sclk <= nxt_sclk;
endmodule
+91
View File
@@ -0,0 +1,91 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// This module forms the qualification engine for a single master as
// part of a larger arbitration engine for a slave. It would typically
// be instantiated from arb_select_master.v to form a complete arbitor solution.
//
module arb_qualify_master
#(
parameter WIDTH=16 // Bit width of destination field.
)
(
input clk,
input reset,
input clear,
// Header signals
input [WIDTH-1:0] header,
input header_valid,
// Slave Confg Signals
input [WIDTH-1:0] slave_addr,
input [WIDTH-1:0] slave_mask,
input slave_valid,
// Arbitration flags
output reg master_valid,
input master_ack
);
localparam WAIT_HEADER_VALID = 0;
localparam MATCH = 1;
localparam WAIT_HEADER_NOT_VALID = 2;
reg [1:0] state, next_state;
// Does masked slave address match header field for dest from master?
assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid;
always @(posedge clk)
if (reset | clear) begin
state <= WAIT_HEADER_VALID;
master_valid <= 0;
end else
begin
case(state)
//
// Wait here until Masters FIFO presents a valid header word.
//
WAIT_HEADER_VALID: begin
if (header_valid)
if (header_match) begin
state <= MATCH;
master_valid <= 1;
end else
next_state <= WAIT_HEADER_NOT_VALID;
end
//
// There should only ever be one match across various arbitors
// if they are configured correctly and since the backing FIFO in the
// master should not start to drain until the arbitration is won
// by that master, master_ack should always preceed de-assertion of
// header_valid so we don't check for the other order of deassertion.
//
MATCH: begin
if (master_ack) begin
master_valid <= 0;
state <= WAIT_HEADER_NOT_VALID;
end
end
//
// Wait here until this master starts to drain this packet from his FIFO.
//
WAIT_HEADER_NOT_VALID: begin
if (!header_valid) begin
state <= WAIT_HEADER_VALID;
end
end
endcase // case(state)
end // else: !if(reset | clear)
endmodule // arb_qualify_master
+164
View File
@@ -0,0 +1,164 @@
/////////////////////////////////////////////////////////////////////
//
// Copyright 2012 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_crossbar
// Description:
// - Control Registers
// - CAM to setup routing between RFNoC blocks
//
/////////////////////////////////////////////////////////////////////
module axi_crossbar
#(
parameter BASE = 0, // settings bus base address
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
parameter NUM_OUTPUTS = 2 // number of output AXI4-STREAM buses
)
(
input clk,
input reset,
input clear,
input [7:0] local_addr,
// Inputs
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
input [NUM_INPUTS-1:0] i_tvalid,
input [NUM_INPUTS-1:0] i_tlast,
output [NUM_INPUTS-1:0] i_tready,
input [NUM_INPUTS-1:0] pkt_present,
// Setting Bus
input set_stb,
input [15:0] set_addr,
input [31:0] set_data,
// Output
output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
output [NUM_OUTPUTS-1:0] o_tvalid,
output [NUM_OUTPUTS-1:0] o_tlast,
input [NUM_OUTPUTS-1:0] o_tready,
// readback bus
input rb_rd_stb,
input [$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:0] rb_addr,
output reg [31:0] rb_data
);
genvar m,n;
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_in;
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_in;
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_out;
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_out;
wire [NUM_INPUTS-1:0] i_tready_slave [0:NUM_OUTPUTS-1];
//
// Instantiate an axi_slave_mux for every slave/output of the Crossbar switch.
// Each axi_slave_mux contains logic to maux and resolve arbitration
// for this particular slave/output.
//
generate
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: instantiate_slave_mux
wire [NUM_INPUTS-1:0] i_tready_tmp;
axi_slave_mux
#(
.FIFO_WIDTH(FIFO_WIDTH), // AXI4-STREAM data bus width
.DST_WIDTH(DST_WIDTH), // Width of DST field we are routing on.
.NUM_INPUTS(NUM_INPUTS) // number of input AXI buses
) axi_slave_mux_i
(
.clk(clk),
.reset(reset),
.clear(clear),
// Inputs
.i_tdata(i_tdata),
.i_tvalid(i_tvalid),
.i_tlast(i_tlast),
.i_tready(i_tready_tmp),
// Forwarding flags (One from each Input/Master)
.forward_valid(forward_valid_in[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
.forward_ack(forward_ack_out[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
// Output
.o_tdata(o_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
.o_tvalid(o_tvalid[m]),
.o_tlast(o_tlast[m]),
.o_tready(o_tready[m])
);
if (m==0)
assign i_tready_slave[0] = i_tready_tmp;
else
assign i_tready_slave[m] = i_tready_tmp | i_tready_slave[m-1] ;
end // block: instantiate_slave_mux
endgenerate
assign i_tready = i_tready_slave[NUM_OUTPUTS-1];
//
// Permute the forwarding flag buses
//
generate
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: permute_outer
for (n = 0; n < NUM_INPUTS; n = n + 1) begin: permute_inner
assign forward_valid_in[n*NUM_OUTPUTS+m] = forward_valid_out[n+m*NUM_INPUTS];
assign forward_ack_in[n+m*NUM_INPUTS] = forward_ack_out[n*NUM_OUTPUTS+m];
end
end
endgenerate
//
// Instantiate an axi_forwarding_cam for every Input/Master of the Crossbar switch.
// Each contains a TCAM like lookup that allocates an egress port.
//
wire [31:0] rb_data_mux[0:NUM_INPUTS-1];
generate
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: instantiate_cam
axi_forwarding_cam
#(
.BASE(BASE),
.WIDTH(FIFO_WIDTH), // Bit width of FIFO word.
.NUM_OUTPUTS(NUM_OUTPUTS)
) axi_forwarding_cam_i
(
.clk(clk),
.reset(reset),
.clear(clear),
// Monitored FIFO signals
.o_tdata(i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
.o_tvalid(i_tvalid[m]),
.o_tready(i_tready[m]),
.o_tlast(i_tlast[m]),
.pkt_present(pkt_present[m]),
// Configuration
.local_addr(local_addr),
// Setting Bus
.set_stb(set_stb),
.set_addr(set_addr),
.set_data(set_data),
// Header signals
.forward_valid(forward_valid_out[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
.forward_ack(forward_ack_in[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
// Readback bus
.rb_rd_stb(rb_rd_stb && (rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)] == m)),
.rb_addr(rb_addr[$clog2(NUM_OUTPUTS)-1:0]),
.rb_data(rb_data_mux[m])
);
end // block: instantiate_fifo_header
endgenerate
// Pipeline readback data to alleviate timing issues
always @(posedge clk) rb_data <= rb_data_mux[rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)]];
endmodule // axi_crossbar
+87
View File
@@ -0,0 +1,87 @@
//
// Copyright 2015 Ettus Research LLC
// Copyright 2017 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Wraps AXI crossbar and exposes cvita_stream_t and settings_bus_t interfaces
`include "sim_cvita_lib.svh"
`include "sim_set_rb_lib.svh"
module axi_crossbar_intf
#(
parameter BASE = 0, // settings bus base address
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
parameter NUM_PORTS = 2 // number of cvita busses
)(
input clk,
input reset,
input clear,
input [7:0] local_addr,
axis_t.slave s_cvita[0:NUM_PORTS-1],
axis_t.master m_cvita[0:NUM_PORTS-1],
settings_bus_t.slave set_bus,
readback_bus_t.master rb_bus
);
wire [NUM_PORTS*64-1:0] flat_i_tdata;
wire [NUM_PORTS-1:0] i_tlast, i_tvalid, i_tready;
wire [NUM_PORTS*64-1:0] flat_o_tdata;
wire [NUM_PORTS-1:0] o_tlast, o_tvalid, o_tready;
// Flattern CE tdata arrays
genvar i;
generate
for (i = 0; i < NUM_PORTS; i = i + 1) begin
assign flat_i_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH] = s_cvita[i].tdata;
assign i_tlast[i] = s_cvita[i].tlast;
assign i_tvalid[i] = s_cvita[i].tvalid;
assign s_cvita[i].tready = i_tready[i];
end
for (i = 0; i < NUM_PORTS; i = i + 1) begin
assign m_cvita[i].tdata = flat_o_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH];
assign m_cvita[i].tlast = o_tlast[i];
assign m_cvita[i].tvalid = o_tvalid[i];
assign o_tready[i] = m_cvita[i].tready;
end
endgenerate
wire set_stb = set_bus.stb;
wire [15:0] set_addr = set_bus.addr;
wire [31:0] set_data = set_bus.data;
wire rb_rd_stb = rb_bus.stb;
wire [2*$clog2(NUM_PORTS):0] rb_addr = rb_bus.addr[2*$clog2(NUM_PORTS):0];
wire [31:0] rb_data;
assign rb_bus.data = rb_data;
axi_crossbar #(
.BASE(BASE),
.FIFO_WIDTH(FIFO_WIDTH),
.DST_WIDTH(DST_WIDTH),
.NUM_INPUTS(NUM_PORTS),
.NUM_OUTPUTS(NUM_PORTS))
inst_axi_crossbar (
.clk(clk),
.reset(reset),
.clear(clear),
.local_addr(local_addr),
.i_tdata(flat_i_tdata),
.i_tvalid(i_tvalid),
.i_tlast(i_tlast),
.i_tready(i_tready),
.pkt_present(i_tvalid),
.set_stb(set_stb),
.set_addr(set_addr),
.set_data(set_data),
.o_tdata(flat_o_tdata),
.o_tvalid(o_tvalid),
.o_tlast(o_tlast),
.o_tready(o_tready),
.rb_rd_stb(rb_rd_stb),
.rb_addr(rb_addr),
.rb_data(rb_data));
endmodule
+193
View File
@@ -0,0 +1,193 @@
/////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_crossbar_regport
// Description:
// - axi_crossbar with regport interface for register/CAM access
//
//////////////////////////////////////////////////////////////////////
module axi_crossbar_regport #(
parameter REG_BASE = 0, // settings bus base address
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
parameter NUM_OUTPUTS = 2, // number of output AXI4-STREAM buses
parameter REG_DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
parameter REG_AWIDTH = 14 // Width of the address bus
)(
input clk,
input reset,
input clear,
input reg_wr_req,
input [REG_AWIDTH-1:0] reg_wr_addr,
input [REG_DWIDTH-1:0] reg_wr_data,
input reg_rd_req,
input [REG_AWIDTH-1:0] reg_rd_addr,
output [REG_DWIDTH-1:0] reg_rd_data,
output reg_rd_resp,
// Inputs
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
input [NUM_INPUTS-1:0] i_tvalid,
input [NUM_INPUTS-1:0] i_tlast,
output [NUM_INPUTS-1:0] i_tready,
input [NUM_INPUTS-1:0] pkt_present,
// Output
output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
output [NUM_OUTPUTS-1:0] o_tvalid,
output [NUM_OUTPUTS-1:0] o_tlast,
input [NUM_OUTPUTS-1:0] o_tready
);
localparam XBAR_VERSION = 32'b1;
localparam XBAR_NUM_PORTS = NUM_INPUTS; //or NUM_OUTPUTS
localparam REG_XBAR_VERSION = REG_BASE + 14'h10;
localparam REG_XBAR_NUM_PORTS = REG_BASE + 14'h14;
localparam REG_XBAR_LOCAL_ADDR = REG_BASE + 14'h18;
localparam REG_BASE_XBAR_SETTING_REG = REG_BASE + 14'h20;
localparam REG_END_ADDR_XBAR_SETTING_REG = REG_BASE + 14'h1000;
// Settings bus address width
localparam SR_AWIDTH = 12;
wire xbar_set_stb;
wire [REG_DWIDTH-1:0] xbar_set_data;
wire [SR_AWIDTH-1:0] xbar_set_addr;
wire xbar_rb_stb;
wire [SR_AWIDTH-1:0] xbar_rb_addr;
wire [REG_DWIDTH-1:0] xbar_rb_data;
reg [31:0] local_addr_reg;
reg reg_rd_resp_glob;
reg [REG_DWIDTH-1:0] reg_rd_data_glob;
wire [REG_DWIDTH-1:0] reg_rd_data_xbar;
wire reg_rd_resp_xbar;
regport_resp_mux #(
.WIDTH(REG_DWIDTH),
.NUM_SLAVES(2)
) inst_regport_resp_mux_xbar (
.clk(clk),
.reset(reset),
.sla_rd_resp({reg_rd_resp_glob, reg_rd_resp_xbar}),
.sla_rd_data({reg_rd_data_glob, reg_rd_data_xbar}),
.mst_rd_resp(reg_rd_resp),
.mst_rd_data(reg_rd_data)
);
// Read Registers
always @ (posedge clk) begin
if (reset) begin
local_addr_reg <= 32'h0;
end
else begin
if (reg_wr_req)
case (reg_wr_addr)
REG_XBAR_LOCAL_ADDR:
local_addr_reg <= reg_wr_data;
endcase
end
end
// Write Registers
always @ (posedge clk) begin
if (reset)
reg_rd_resp_glob <= 1'b0;
else begin
if (reg_rd_req) begin
reg_rd_resp_glob <= 1'b1;
case (reg_rd_addr)
REG_XBAR_VERSION:
reg_rd_data_glob <= XBAR_VERSION;
REG_XBAR_NUM_PORTS:
reg_rd_data_glob <= XBAR_NUM_PORTS;
REG_XBAR_LOCAL_ADDR:
reg_rd_data_glob <= local_addr_reg;
default:
reg_rd_resp_glob <= 1'b0;
endcase
end
else if (reg_rd_resp_glob) begin
reg_rd_resp_glob <= 1'b0;
end
end
end
regport_to_xbar_settingsbus #(
.BASE(REG_BASE_XBAR_SETTING_REG),
.END_ADDR(REG_END_ADDR_XBAR_SETTING_REG),
.DWIDTH(REG_DWIDTH),
.AWIDTH(REG_AWIDTH),
.SR_AWIDTH(SR_AWIDTH),
.ADDRESSING("WORD")
) inst_regport_to_xbar_settingsbus (
.clk(clk),
.reset(reset),
.reg_wr_req(reg_wr_req),
.reg_wr_addr(reg_wr_addr),
.reg_wr_data(reg_wr_data),
.reg_rd_req(reg_rd_req),
.reg_rd_addr(reg_rd_addr),
.reg_rd_data(reg_rd_data_xbar),
.reg_rd_resp(reg_rd_resp_xbar),
.set_stb(xbar_set_stb),
.set_addr(xbar_set_addr),
.set_data(xbar_set_data),
.rb_stb(xbar_rb_stb),
.rb_addr(xbar_rb_addr),
.rb_data(xbar_rb_data)
);
axi_crossbar #(
.BASE(0), // Set to 0 as logic for other values has not been tested
.FIFO_WIDTH(FIFO_WIDTH),
.DST_WIDTH(DST_WIDTH),
.NUM_INPUTS(NUM_INPUTS),
.NUM_OUTPUTS(NUM_OUTPUTS)
) axi_crossbar (
.clk(clk),
.reset(reset),
.clear(1'b0),
.local_addr(local_addr_reg),
// settings bus for config
.set_stb(xbar_set_stb),
.set_addr({4'b0000,xbar_set_addr}),
.set_data(xbar_set_data),
.rb_rd_stb(xbar_rb_stb),
.rb_addr(xbar_rb_addr[$clog2(NUM_INPUTS)+$clog2(NUM_OUTPUTS)-1:0]),
.rb_data(xbar_rb_data),
// inputs, real men flatten busses
.i_tdata(i_tdata),
.i_tlast(i_tlast),
.i_tvalid(i_tvalid),
.i_tready(i_tready),
// outputs, real men flatten busses
.o_tdata(o_tdata),
.o_tlast(o_tlast),
.o_tvalid(o_tvalid),
.o_tready(o_tready),
.pkt_present(pkt_present)
);
endmodule // axi_crossbar_regport
+87
View File
@@ -0,0 +1,87 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data.
// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined
// for timing closure.
//
module axi_fifo_header
#(
parameter WIDTH=64 // Bit width of FIFO word.
)
(
input clk,
input reset,
input clear,
// Monitored FIFO signals
input [WIDTH-1:0] o_tdata,
input o_tvalid,
input o_tready,
input o_tlast,
input pkt_present,
// Header signals
output reg [WIDTH-1:0] header,
output reg header_valid
);
localparam WAIT_SOF = 0;
localparam WAIT_EOF = 1;
reg out_state;
//
// Monitor packets leaving FIFO
//
always @(posedge clk)
if (reset | clear) begin
out_state <= WAIT_SOF;
end else
case(out_state)
//
// After RESET or the EOF of previous packet, the first cycle with
// output valid asserted is the SOF and presents the Header word.
// The cycle following the concurrent presentation of asserted output
// valid and output ready presents the word following the header.
//
WAIT_SOF:
if (o_tvalid && o_tready) begin
out_state <= WAIT_EOF;
end else begin
out_state <= WAIT_SOF;
end
//
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
//
WAIT_EOF:
if (o_tlast && o_tvalid && o_tready) begin
out_state <= WAIT_SOF;
end else begin
out_state <= WAIT_EOF;
end
endcase // case(in_state)
//
// Pipeline Header signals
//
always @(posedge clk)
if (reset | clear) begin
header <= 0;
header_valid <= 0;
end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin
// Header will remian valid until o_tready is asserted as this will cause a state transition.
header <= o_tdata;
header_valid <= 1;
end else begin
header_valid <= 0;
end
endmodule // axi_fifo_header
+232
View File
@@ -0,0 +1,232 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This module implements a highly customized content-addressable memory (CAM)
// that enables forwarding decisions to be made on a 16 bit field from a stream ID (SID) field.
// The forwarding is generic in the sense that a SID's host destination can map to any endpoint / crossbar port.
//
// The 16 bits are allocated by convention as 8 bits of Network address (addresses USRP's / AXI crossbars) and
// 8 bits of Host address (addresses endpoints / crossbar ports in a USRP).
//
// By definition if the destination field in the SID addresses a different
// USRP / crossbar than this one then we don't care about the Host field, only the Network field.
// We only look at the Host field when the Network field addresses us.
// Thus we need a CAM of 256+256 entries with Log2(N) bits, where N is the number of
// slave(output) ports on the crossbar switch.
//
// SID format:
//
// |---------|---------|---------|---------|
// | SRC | SRC | DST | DST |
// | NETWORK | HOST | NETWORK | HOST |
// |---------|---------|---------|---------|
// 8 8 8 8
module axi_forwarding_cam
#(
parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
parameter WIDTH=64, // Bit width of FIFO word.
parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
)
(
input clk,
input reset,
input clear,
// Monitored FIFO signals
input [WIDTH-1:0] o_tdata,
input o_tvalid,
input o_tready,
input o_tlast,
input pkt_present,
// Configuration
input [7:0] local_addr,
// Setting Bus
input set_stb,
input [15:0] set_addr,
input [31:0] set_data,
output reg [NUM_OUTPUTS-1:0] forward_valid,
input [NUM_OUTPUTS-1:0] forward_ack,
input rb_rd_stb,
input [$clog2(NUM_OUTPUTS)-1:0] rb_addr,
output [31:0] rb_data
);
localparam WAIT_SOF = 0;
localparam WAIT_EOF = 1;
reg state;
localparam IDLE = 0;
localparam FORWARD = 1;
localparam WAIT = 2;
reg [1:0] demux_state;
reg [15:0] dst;
reg dst_valid, dst_valid_reg;
wire local_dst;
wire [8:0] read_addr;
//
// Monitor packets leaving FIFO
//
always @(posedge clk)
if (reset | clear) begin
state <= WAIT_SOF;
end else
case(state)
//
// After RESET or the EOF of previous packet, the first cycle with
// output valid asserted is the SOF and presents the Header word.
// The cycle following the concurrent presentation of asserted output
// valid and output ready presents the word following the header.
//
WAIT_SOF:
if (o_tvalid && o_tready) begin
state <= WAIT_EOF;
end else begin
state <= WAIT_SOF;
end
//
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
//
WAIT_EOF:
if (o_tlast && o_tvalid && o_tready) begin
state <= WAIT_SOF;
end else begin
state <= WAIT_EOF;
end
endcase // case(in_state)
//
// Extract Destination fields(s) from SID
//
always @(posedge clk)
if (reset | clear) begin
dst <= 0;
dst_valid <= 0;
dst_valid_reg <= 0;
end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
// SID will remain valid until o_tready is asserted as this will cause a state transition.
dst <= o_tdata[15:0];
dst_valid <= 1;
dst_valid_reg <= dst_valid;
end else begin
dst_valid <= 0;
dst_valid_reg <= dst_valid;
end
//
// Is Network field in DST our local address?
//
assign local_dst = (dst[15:8] == local_addr) && dst_valid;
//
// Mux address to RAM so that it searches CAM for Network field or Host field.
// Network addresses are stored in the lower 256 locations, host addresses the upper 256.
//
assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
//
// Implement CAM as block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
//
//synthesis attribute ram_style of mem is block
reg [$clog2(NUM_OUTPUTS)-1 : 0] mem [0:511];
// Initialize the CAM's local address forwarding decisions with sensible defaults by
// assuming dst[7:4] = crossbar port, dst[3:0] = block port. Setup a one-to-one mapping
// for crossbar ports and always map same crossbar port regardless of block port.
// i.e.
// dst 8'h00 => forward to crossbar port 0
// dst 8'h01 => forward to crossbar port 0
// dst 8'h10 => forward to crossbar port 1
// etc.
integer xbar_port;
integer block_port;
initial begin
for (xbar_port = 0; xbar_port < NUM_OUTPUTS; xbar_port = xbar_port + 1) begin
for (block_port = 0; block_port < 16; block_port = block_port + 1) begin
mem[256+(xbar_port << 4)+block_port] = xbar_port;
end
end
end
reg [8:0] read_addr_reg;
wire write;
wire [$clog2(NUM_OUTPUTS)-1:0] read_data;
assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
always @(posedge clk)
begin
read_addr_reg <= read_addr;
if (write) begin
mem[set_addr[8:0]] <= set_data[$clog2(NUM_OUTPUTS)-1:0];
end
end
assign read_data = mem[read_addr_reg];
//
// State machine to manage forwarding flags.
//
always @(posedge clk)
if (reset | clear) begin
forward_valid <= {NUM_OUTPUTS{1'b0}};
demux_state <= IDLE;
end else
case(demux_state)
// Wait for Valid DST which indicates a new packet lookup in the CAM.
IDLE: begin
if (dst_valid_reg == 1) begin
forward_valid <= 1'b1 << read_data;
demux_state <= FORWARD;
end
end
// When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
FORWARD: begin
if ((forward_ack & forward_valid) != 0) begin
forward_valid <= {NUM_OUTPUTS{1'b0}};
demux_state <= WAIT;
end
end
// When packet transfered go back to idle.
WAIT: begin
if (forward_ack == 0)
demux_state <= IDLE;
end
endcase // case (demux_state)
//
// Compile forwarding statistics
// (This uses a lot of registers!)
//
genvar m;
reg [31:0] statistics [0:NUM_OUTPUTS-1];
generate
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
always @(posedge clk) begin
if (reset | clear) begin
statistics[m] <= 0;
end else if (forward_ack[m] & forward_valid[m]) begin
statistics[m] <= statistics[m] + 1;
end
end
end
endgenerate
assign rb_data = statistics[rb_addr];
endmodule
+97
View File
@@ -0,0 +1,97 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Settings register with AXI stream output.
//
// Parameters / common use cases:
// USE_ADDR_LAST & ADDR_LAST User wants additional address that when written to asserts tlast.
// Useful for the last word in a packet.
// USE_FIFO & FIFO_SIZE Downstream block can throttle and a FIFO is needed to handle that case.
// STROBE_LAST User always wants to assert tlast on writes. More efficient than USE_ADDR_LAST
// since only one address is used instead of two.
// REPEATS Keep tvalid asserted after initial write.
// STROBE_LAST & REPEATS tlast is asserted on the initial write then deasserted for repeating output.
// MSB_ALIGN Left justify data versus right justify.
module axi_setting_reg #(
parameter ADDR = 0,
parameter USE_ADDR_LAST = 0,
parameter ADDR_LAST = ADDR+1,
parameter AWIDTH = 8,
parameter WIDTH = 32,
parameter USE_FIFO = 0,
parameter FIFO_SIZE = 5,
parameter DATA_AT_RESET = 0,
parameter VALID_AT_RESET = 0,
parameter LAST_AT_RESET = 0,
parameter STROBE_LAST = 0,
parameter REPEATS = 0,
parameter MSB_ALIGN = 0
)
(
input clk, input reset, output reg error_stb,
input set_stb, input [AWIDTH-1:0] set_addr, input [31:0] set_data,
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
);
reg init;
reg [WIDTH-1:0] o_tdata_int;
reg o_tlast_int, o_tvalid_int;
wire o_tready_int;
always @(posedge clk) begin
if (reset) begin
o_tdata_int <= DATA_AT_RESET;
o_tvalid_int <= VALID_AT_RESET;
o_tlast_int <= LAST_AT_RESET;
init <= 1'b0;
error_stb <= 1'b0;
end else begin
error_stb <= 1'b0;
if (o_tvalid_int & o_tready_int) begin
// Deassert tvalid / tlast only if not repeating the output
if (REPEATS == 0) begin
o_tvalid_int <= 1'b0;
end
if ((REPEATS == 0) | (STROBE_LAST == 1)) begin
o_tlast_int <= 1'b0;
end
end
if (set_stb & ((ADDR[AWIDTH-1:0] == set_addr) | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
init <= 1'b1;
o_tdata_int <= (MSB_ALIGN == 0) ? set_data[WIDTH-1:0] : set_data[31:32-WIDTH];
o_tvalid_int <= 1'b1;
if (set_stb & (STROBE_LAST | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
o_tlast_int <= 1'b1;
end else begin
o_tlast_int <= 1'b0;
end
if (~o_tready_int) begin
error_stb <= 1'b1;
end
end
end
end
generate
if (USE_FIFO) begin
axi_fifo #(
.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE))
axi_fifo (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
.space(), .occupied());
end else begin
assign o_tdata = o_tdata_int;
assign o_tlast = o_tlast_int;
assign o_tvalid = o_tvalid_int;
assign o_tready_int = o_tready;
end
endgenerate
endmodule
+122
View File
@@ -0,0 +1,122 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`ifndef LOG2
`define LOG2(N) (\
N < 2 ? 0 : \
N < 4 ? 1 : \
N < 8 ? 2 : \
N < 16 ? 3 : \
N < 32 ? 4 : \
N < 64 ? 5 : \
N < 128 ? 6 : \
N < 256 ? 7 : \
N < 512 ? 8 : \
N < 1024 ? 9 : 10)
`endif
module axi_slave_mux
#(
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
parameter NUM_INPUTS = 2 // number of input AXI buses
)
(
input clk,
input reset,
input clear,
// Inputs
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
input [NUM_INPUTS-1:0] i_tvalid,
input [NUM_INPUTS-1:0] i_tlast,
output [NUM_INPUTS-1:0] i_tready,
// Forwarding Flags
input [NUM_INPUTS-1:0] forward_valid,
output reg [NUM_INPUTS-1:0] forward_ack,
// Output
output [FIFO_WIDTH-1:0] o_tdata,
output o_tvalid,
output o_tlast,
input o_tready
);
wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1];
reg [`LOG2(NUM_INPUTS):0] select;
reg enable;
reg state;
localparam CHECK_THIS_INPUT = 0;
localparam WAIT_LAST = 1;
always @(posedge clk)
if (reset | clear) begin
state <= CHECK_THIS_INPUT;
select <= 0;
enable <= 0;
forward_ack <= 0;
end else begin
case(state)
// Is the currently selected input addressing this slave with a ready packet?
CHECK_THIS_INPUT: begin
if (forward_valid[select]) begin
enable <= 1;
forward_ack[select] <= 1;
state <= WAIT_LAST;
end else if (select == NUM_INPUTS - 1 ) begin
select <= 0;
end else begin
select <= select + 1;
end
end
// Assert ACK immediately to forwarding logic and then wait for end of packet.
WAIT_LAST: begin
if (i_tlast[select] && i_tvalid[select] && o_tready) begin
if (select == NUM_INPUTS - 1 ) begin
select <= 0;
end else begin
select <= select + 1;
end
state <= CHECK_THIS_INPUT;
forward_ack <= 0;
enable <= 0;
end else begin
forward_ack[select] <= 1;
enable <= 1;
end
end
endcase // case(state)
end
//
// Combinatorial mux
//
genvar m;
generate
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses
assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH];
end
endgenerate
assign o_tdata = i_tdata_array[select];
assign o_tvalid = enable && i_tvalid[select];
assign o_tlast = enable && i_tlast[select];
// assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select);
generate
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready
assign i_tready[m] = o_tready && enable && (select == m);
end
endgenerate
endmodule // axi_slave_mux
+145
View File
@@ -0,0 +1,145 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper
// and lower 32bits). Test result by tracking count on receive and using
// sticky flag for error indication.
// Also provide signal from MSB of 32bit count to blink LED.
//
module axi_test_vfifo
#(parameter PACKET_SIZE = 128)
(
input aclk,
input aresetn,
input enable,
// AXI Stream Out
output reg out_axis_tvalid,
input out_axis_tready,
output [63 : 0] out_axis_tdata,
output reg [7 : 0] out_axis_tstrb,
output reg [7 : 0] out_axis_tkeep,
output reg out_axis_tlast,
output reg [0 : 0] out_axis_tid,
output reg [0 : 0] out_axis_tdest,
input vfifo_full,
// AXI Stream In
input in_axis_tvalid,
output reg in_axis_tready,
input [63 : 0] in_axis_tdata,
input [7 : 0] in_axis_tstrb,
input [7 : 0] in_axis_tkeep,
input in_axis_tlast,
input [0 : 0] in_axis_tid,
input [0 : 0] in_axis_tdest,
// Flags
output reg flag_error,
output heartbeat_in,
output heartbeat_out,
output [31:0] expected_count
);
reg [31:0] out_count;
reg [31:0] in_count;
reg [63:0] in_axis_tdata_reg;
reg in_data_valid;
//
// Output
//
always @(posedge aclk)
if (!aresetn) begin
out_count <= 0;
out_axis_tvalid <= 0;
out_axis_tid <= 0; // Don't care.
out_axis_tdest <= 0; // Only use port 0 of VFIFO.
out_axis_tstrb <= 0; // Unused in VFIFO
out_axis_tkeep <= 8'hFF; // Always use every byte of data
out_axis_tlast <= 1'b0;
end else if (enable) begin
if (~vfifo_full) begin
// Always ready to output new count value.
out_axis_tvalid <= 1;
if (out_axis_tready)
out_count <= out_count + 1;
// Assert TLAST every PACKET_SIZE beats.
if (out_count[15:0] == PACKET_SIZE)
out_axis_tlast <= 1'b1;
else
out_axis_tlast <= 1'b0;
end else begin
out_axis_tvalid <= 0;
end
end else begin
out_axis_tlast <= 1'b0;
out_axis_tvalid <= 0;
end
assign out_axis_tdata = {out_count,out_count};
assign heartbeat_out = out_count[28];
//
// Input (Ignore TLAST signal)
//
always @(posedge aclk)
if (!aresetn) begin
in_axis_tready <= 0;
in_axis_tdata_reg <= 0;
in_data_valid <= 0;
end else if (enable) begin
in_axis_tready <= 1;
in_axis_tdata_reg <= in_axis_tdata;
if (in_axis_tvalid)
in_data_valid <= 1;
else
in_data_valid <= 0;
end else begin
in_data_valid <= 0;
in_axis_tready <= 0;
end // else: !if(enable)
assign heartbeat_in = in_count[28];
//
// Input Checker
//
always @(posedge aclk)
if (!aresetn) begin
in_count <= 0;
flag_error <= 0;
end else if (enable) begin
if (in_data_valid) begin
if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count))
begin
flag_error <= 1;
in_count <= in_axis_tdata_reg[63:32] + 1;
end
else
begin
flag_error <= 0;
in_count <= in_count + 1;
end
end
end
assign expected_count = in_count;
endmodule // axi_test_vfifo
+248
View File
@@ -0,0 +1,248 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axil_ctrlport_master
// Description:
// An AXI4-Lite read/write control port adapter
//
// Converts AXI4-Lite transactions into control port requests.
// Converts all AXI requests to control port by only forwarding the
// CTRLPORT_AWIDTH LSBs of the address.
//
// Limitation:
// The control port interface will only use address, data, byte enable and
// wr/rd flags. All other signals are tied to 0.
module axil_ctrlport_master #(
parameter TIMEOUT = 10, // log2(timeout). Control port will timeout after 2^TIMEOUT AXI clock cycles
parameter AXI_AWIDTH = 17, // Width of the AXI bus. Aliasing occurs of AXI_AWIDTH > CTRLPORT_AWIDTH
parameter CTRLPORT_AWIDTH = 17 // Number of address LSBs forwarded to m_ctrlport_req_addr
)(
//Clock and reset
input wire s_axi_aclk,
input wire s_axi_aresetn,
// AXI4-Lite: Write address port (domain: s_axi_aclk)
input wire [AXI_AWIDTH-1:0] s_axi_awaddr,
input wire s_axi_awvalid,
output reg s_axi_awready,
// AXI4-Lite: Write data port (domain: s_axi_aclk)
input wire [31:0] s_axi_wdata,
input wire [ 3:0] s_axi_wstrb,
input wire s_axi_wvalid,
output reg s_axi_wready,
// AXI4-Lite: Write response port (domain: s_axi_aclk)
output reg [ 1:0] s_axi_bresp = 0,
output reg s_axi_bvalid,
input wire s_axi_bready,
// AXI4-Lite: Read address port (domain: s_axi_aclk)
input wire [AXI_AWIDTH-1:0] s_axi_araddr,
input wire s_axi_arvalid,
output reg s_axi_arready,
// AXI4-Lite: Read data port (domain: s_axi_aclk)
output reg [31:0] s_axi_rdata = 0,
output reg [ 1:0] s_axi_rresp = 0,
output reg s_axi_rvalid,
input wire s_axi_rready,
// Control port master request interface
output reg m_ctrlport_req_wr,
output reg m_ctrlport_req_rd,
output reg [19:0] m_ctrlport_req_addr = 0,
output wire [ 9:0] m_ctrlport_req_portid,
output wire [15:0] m_ctrlport_req_rem_epid,
output wire [ 9:0] m_ctrlport_req_rem_portid,
output reg [31:0] m_ctrlport_req_data = 0,
output reg [ 3:0] m_ctrlport_req_byte_en = 0,
output wire m_ctrlport_req_has_time,
output wire [63:0] m_ctrlport_req_time,
// Control port master response interface
input wire m_ctrlport_resp_ack,
input wire [ 1:0] m_ctrlport_resp_status,
input wire [31:0] m_ctrlport_resp_data
);
`include "../axi/axi_defs.v"
`include "../rfnoc/core/ctrlport.vh"
//----------------------------------------------------------
// unused ctrlport outputs
//----------------------------------------------------------
assign m_ctrlport_req_portid = 10'b0;
assign m_ctrlport_req_rem_epid = 16'b0;
assign m_ctrlport_req_rem_portid = 10'b0;
assign m_ctrlport_req_has_time = 1'b0;
assign m_ctrlport_req_time = 64'b0;
//----------------------------------------------------------
// Address calculation
//----------------------------------------------------------
// define configuration for the address calculation
localparam [CTRLPORT_ADDR_W-1:0] ADDRESS_MASK = {CTRLPORT_ADDR_W {1'b0}} | {CTRLPORT_AWIDTH {1'b1}};
// bits to extract from AXI address
localparam AXI_ADDR_BITS_TO_FORWARD = (AXI_AWIDTH < CTRLPORT_ADDR_W) ? AXI_AWIDTH : CTRLPORT_ADDR_W;
//----------------------------------------------------------
// State machine for read and write
//----------------------------------------------------------
localparam IDLE = 4'd0;
localparam READ_INIT = 4'd1;
localparam WRITE_INIT = 4'd2;
localparam READ_TRANSFER = 4'd3;
localparam WRITE_TRANSFER = 4'd4;
localparam READ_IN_PROGRESS = 4'd5;
localparam WRITE_IN_PROGRESS = 4'd6;
localparam WRITE_DONE = 4'd7;
localparam READ_DONE = 4'd8;
reg [3:0] state;
reg [TIMEOUT-1:0] timeout_counter;
always @ (posedge s_axi_aclk) begin
if (~s_axi_aresetn) begin
state <= IDLE;
// clear AXI feedback paths and controlport requests
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
s_axi_arready <= 1'b0;
s_axi_rvalid <= 1'b0;
m_ctrlport_req_rd <= 1'b0;
m_ctrlport_req_wr <= 1'b0;
end else begin
case (state)
// decide whether a read or write should be handled
IDLE: begin
timeout_counter <= {TIMEOUT {1'b1}};
if (s_axi_arvalid) begin
state <= READ_INIT;
end
else if (s_axi_awvalid) begin
state <= WRITE_INIT;
end
end
// wait for FIFO to get read to assign valid
READ_INIT: begin
// signal ready to upstream module
s_axi_arready <= 1'b1;
state <= READ_TRANSFER;
end
// transfer data to FIFO
READ_TRANSFER: begin
// clear ready flag from READ_INIT state
s_axi_arready <= 1'b0;
// transfer data to controlport
m_ctrlport_req_rd <= 1'b1;
m_ctrlport_req_addr <= s_axi_araddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
m_ctrlport_req_byte_en <= 4'b1111;
state <= READ_IN_PROGRESS;
end
// wait for controlport response is available
READ_IN_PROGRESS: begin
// clear read flag from previous state
m_ctrlport_req_rd <= 1'b0;
//decrement timeout
timeout_counter <= timeout_counter - 1;
if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
s_axi_rvalid <= 1'b1;
s_axi_rdata <= m_ctrlport_resp_data;
s_axi_rresp <= `AXI4_RESP_OKAY;
// use AXI DECERR to inform about failed transaction
if (timeout_counter == 0) begin
s_axi_rresp <= `AXI4_RESP_DECERR;
end else begin
// if controlport response is not OKAY use AXI SLVERR to propagate error
if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
s_axi_rresp <= `AXI4_RESP_SLVERR;
end
end
state <= READ_DONE;
end
end
// wait until read response is transferred
READ_DONE: begin
if (s_axi_rready) begin
s_axi_rvalid <= 1'b0;
state <= IDLE;
end
end
//wait for FIFO and data to process
WRITE_INIT: begin
if (s_axi_wvalid) begin
s_axi_awready <= 1'b1;
s_axi_wready <= 1'b1;
state <= WRITE_TRANSFER;
end
end
// transfer data to FIFO
WRITE_TRANSFER: begin
// clear ready flags from READ_INIT state
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b0;
// transfer data to controlport
m_ctrlport_req_wr <= 1'b1;
m_ctrlport_req_addr <= s_axi_awaddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
m_ctrlport_req_data <= s_axi_wdata;
m_ctrlport_req_byte_en <= s_axi_wstrb;
state <= WRITE_IN_PROGRESS;
end
// wait for write to complete
WRITE_IN_PROGRESS: begin
// clear write flag from previous state
m_ctrlport_req_wr <= 1'b0;
//decrement timeout
timeout_counter <= timeout_counter - 1;
if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
s_axi_bvalid <= 1'b1;
s_axi_rdata <= 32'b0;
s_axi_bresp <= `AXI4_RESP_OKAY;
// use AXI DECERR to inform about failed transaction
if (timeout_counter == 0) begin
s_axi_bresp <= `AXI4_RESP_DECERR;
end else begin
// if controlport response is not OKAY use AXI SLVERR to propagate error
if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
s_axi_bresp <= `AXI4_RESP_SLVERR;
end
end
state <= WRITE_DONE;
end
end
WRITE_DONE: begin
if (s_axi_bready) begin
state <= IDLE;
s_axi_bvalid <= 1'b0;
end
end
default: begin
state <= IDLE;
end
endcase
end
end
endmodule
+228
View File
@@ -0,0 +1,228 @@
//
// Copyright 2016-2017 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// An AXI4-Lite read/write register port adapter
//
// Converts memory mapped flow controlled AXI4-Lite transactions into a much
// simpler non flow controlled write and read register bus.
//
// WRITE Transaction:
// - Transaction completes in one cycle
// - Valid, Strobe, Address and Data asserted in same cycle
// __ __ __ __
// clk __| |__| |__| |__| |__
// _____
// reg_wr_req ________| |___________
// _____
// reg_wr_keep XXXXXXXX|_____|XXXXXXXXXXX
// _____
// reg_wr_addr XXXXXXXX|_____|XXXXXXXXXXX
// _____
// reg_wr_data XXXXXXXX|_____|XXXXXXXXXXX
//;
// READ Transaction:
// - Transaction request completes in one cycle, with valid and address assertion
// - Transaction response must complete in at least one cycle with resp and data
// - resp must be asserted between 1 and pow(2, TIMEOUT) cycles otherwise the read will timeout
// __ __ __ __ __
// clk __| |__| |__| |__| |__| |__
// _____
// reg_rd_req ________| |_________________
// _____
// reg_rd_addr XXXXXXXX|_____|XXXXXXXXXXXXXXXXX
// _____
// reg_rd_resp ____________________| |_____
// _____
// reg_rd_data XXXXXXXXXXXXXXXXXXXX|_____|XXXXX
module axil_regport_master #(
parameter DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
parameter AWIDTH = 32, // Width of the address bus
parameter WRBASE = 32'h0, // Write address base
parameter RDBASE = 32'h0, // Read address base
parameter TIMEOUT = 10 // log2(timeout). Read will timeout after (2^TIMEOUT - 1) cycles
)(
// Clock and reset
input s_axi_aclk,
input s_axi_aresetn,
input reg_clk,
// AXI4-Lite: Write address port (domain: s_axi_aclk)
input [AWIDTH-1:0] s_axi_awaddr,
input s_axi_awvalid,
output reg s_axi_awready,
// AXI4-Lite: Write data port (domain: s_axi_aclk)
input [DWIDTH-1:0] s_axi_wdata,
input [DWIDTH/8-1:0] s_axi_wstrb,
input s_axi_wvalid,
output reg s_axi_wready,
// AXI4-Lite: Write response port (domain: s_axi_aclk)
output reg [1:0] s_axi_bresp,
output reg s_axi_bvalid,
input s_axi_bready,
// AXI4-Lite: Read address port (domain: s_axi_aclk)
input [AWIDTH-1:0] s_axi_araddr,
input s_axi_arvalid,
output reg s_axi_arready,
// AXI4-Lite: Read data port (domain: s_axi_aclk)
output reg [DWIDTH-1:0] s_axi_rdata,
output reg [1:0] s_axi_rresp,
output reg s_axi_rvalid,
input s_axi_rready,
// Register port: Write port (domain: reg_clk)
output reg_wr_req,
output [AWIDTH-1:0] reg_wr_addr,
output [DWIDTH-1:0] reg_wr_data,
output [DWIDTH/8-1:0] reg_wr_keep,
// Register port: Read port (domain: reg_clk)
output reg_rd_req,
output [AWIDTH-1:0] reg_rd_addr,
input reg_rd_resp,
input [DWIDTH-1:0] reg_rd_data
);
//NOTE: clog2 only works when assigned to a parameter
// localparam does not work
parameter ADDR_LSB = $clog2(DWIDTH/8); //Do not modify
//----------------------------------------------------------
// Write state machine
//----------------------------------------------------------
reg [AWIDTH-1:0] wr_addr_cache;
wire wr_fifo_valid, wr_fifo_ready;
wire [AWIDTH-1:0] wr_addr_rel = (s_axi_awaddr - WRBASE);
// Generate s_axi_awready and latch write address
always @(posedge s_axi_aclk) begin
if (!s_axi_aresetn) begin
s_axi_awready <= 1'b0;
wr_addr_cache <= {AWIDTH{1'b0}};
end else begin
if (~s_axi_awready && s_axi_awvalid && s_axi_wvalid && wr_fifo_ready) begin
s_axi_awready <= 1'b1;
wr_addr_cache <= {wr_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}};
end else begin
s_axi_awready <= 1'b0;
end
end
end
// Generate s_axi_wready
always @(posedge s_axi_aclk) begin
if (!s_axi_aresetn) begin
s_axi_wready <= 1'b0;
end else begin
if (~s_axi_wready && s_axi_wvalid && s_axi_awvalid)
s_axi_wready <= 1'b1;
else
s_axi_wready <= 1'b0;
end
end
// Generate write response
assign wr_fifo_valid = s_axi_awready && s_axi_awvalid && s_axi_wready && s_axi_wvalid && ~s_axi_bvalid;
always @(posedge s_axi_aclk) begin
if (!s_axi_aresetn) begin
s_axi_bvalid <= 1'b0;
s_axi_bresp <= 2'b0;
end else begin
if (wr_fifo_valid && wr_fifo_ready) begin
// indicates a valid write response is available
s_axi_bvalid <= 1'b1;
s_axi_bresp <= 2'b0; // 'OKAY' response
end else begin
if (s_axi_bready && s_axi_bvalid)
s_axi_bvalid <= 1'b0;
end
end
end
axi_fifo_2clk #( .WIDTH(DWIDTH/8 + AWIDTH + DWIDTH), .SIZE(0) ) wr_fifo_2clk_i (
.reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
.i_tdata({s_axi_wstrb, wr_addr_cache, s_axi_wdata}),
.i_tvalid(wr_fifo_valid), .i_tready(wr_fifo_ready),
.o_aclk(reg_clk),
.o_tdata({reg_wr_keep, reg_wr_addr, reg_wr_data}),
.o_tvalid(reg_wr_req), .o_tready(1'b1)
);
//----------------------------------------------------------
// Read state machine
//----------------------------------------------------------
reg [TIMEOUT-1:0] read_pending_ctr = {TIMEOUT{1'b0}};
wire read_timed_out = (read_pending_ctr == {{(TIMEOUT-1){1'b0}}, 1'b1});
wire read_pending = (read_pending_ctr != {TIMEOUT{1'b0}});
wire [AWIDTH-1:0] rd_addr_rel = (s_axi_araddr - RDBASE);
wire rdreq_fifo_ready, rdresp_fifo_valid;
wire [DWIDTH-1:0] rdresp_fifo_data;
// Generate s_axi_arready and latch read address
always @(posedge s_axi_aclk) begin
if (!s_axi_aresetn) begin
s_axi_arready <= 1'b0;
read_pending_ctr <= {TIMEOUT{1'b0}};
end else begin
if (~s_axi_arready && s_axi_arvalid && rdreq_fifo_ready) begin
s_axi_arready <= 1'b1;
read_pending_ctr <= {TIMEOUT{1'b1}};
end else begin
s_axi_arready <= 1'b0;
end
if (read_pending) begin
if (rdresp_fifo_valid && ~s_axi_rvalid)
read_pending_ctr <= {TIMEOUT{1'b0}};
else
read_pending_ctr <= read_pending_ctr - 1'b1;
end
end
end
// Perform read transaction
always @(posedge s_axi_aclk) begin
if (!s_axi_aresetn) begin
s_axi_rvalid <= 1'b0;
s_axi_rresp <= 2'b00;
s_axi_rdata <= 0;
end else begin
if (read_pending && rdresp_fifo_valid && ~s_axi_rvalid) begin
// Valid read data is available at the read data bus
s_axi_rvalid <= 1'b1;
s_axi_rresp <= 2'b00; // 'OKAY' response
s_axi_rdata <= rdresp_fifo_data;
end else if (read_pending && read_timed_out && ~s_axi_rvalid) begin
// Read timed out. Assert error.
s_axi_rvalid <= 1'b1;
s_axi_rresp <= 2'b10; // 'SLVERR' response
s_axi_rdata <= {DWIDTH{1'b1}};
end else if (s_axi_rvalid && s_axi_rready) begin
// Read data is accepted by the master
s_axi_rvalid <= 1'b0;
end
end
end
axi_fifo_2clk #( .WIDTH(AWIDTH), .SIZE(0) ) readreq_fifo_2clk_i (
.reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
.i_tdata({rd_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}}),
.i_tvalid(s_axi_arready && s_axi_arvalid), .i_tready(rdreq_fifo_ready),
.o_aclk(reg_clk),
.o_tdata(reg_rd_addr),
.o_tvalid(reg_rd_req), .o_tready(1'b1)
);
axi_fifo_2clk #( .WIDTH(DWIDTH), .SIZE(0) ) rdresp_fifo_2clk_i (
.reset(~s_axi_aresetn), .i_aclk(reg_clk),
.i_tdata(reg_rd_data),
.i_tvalid(reg_rd_resp), .i_tready(/* lossy */),
.o_aclk(s_axi_aclk),
.o_tdata(rdresp_fifo_data),
.o_tvalid(rdresp_fifo_valid), .o_tready(~read_pending || (s_axi_rvalid && (s_axi_rresp == 2'b00)))
);
endmodule
+164
View File
@@ -0,0 +1,164 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// AXI4lite to NI Register Port interface
//
module axil_to_ni_regport #(
parameter RP_AWIDTH = 16,
parameter RP_DWIDTH = 32,
parameter TIMEOUT = 512
)(
input s_axi_aclk,
input s_axi_areset,
// AXI4lite interface
input [31:0] s_axi_awaddr,
input s_axi_awvalid,
output s_axi_awready,
input [31:0] s_axi_wdata,
input [3:0] s_axi_wstrb,
input s_axi_wvalid,
output s_axi_wready,
output [1:0] s_axi_bresp,
output s_axi_bvalid,
input s_axi_bready,
input [31:0] s_axi_araddr,
input s_axi_arvalid,
output s_axi_arready,
output [31:0] s_axi_rdata,
output [1:0] s_axi_rresp,
output s_axi_rvalid,
input s_axi_rready,
// RegPort interface, the out vs in
// is seen from the slave device
// hooked up to the regport
output reg_port_in_rd,
output reg_port_in_wt,
output [RP_AWIDTH-1:0] reg_port_in_addr,
output [RP_DWIDTH-1:0] reg_port_in_data,
input [RP_DWIDTH-1:0] reg_port_out_data,
input reg_port_out_ready
);
localparam IDLE = 3'd0;
localparam READ_INIT = 3'd1;
localparam WRITE_INIT = 3'd2;
localparam READ_IN_PROGRESS = 3'd3;
localparam WRITE_IN_PROGRESS = 3'd4;
localparam WRITE_DONE = 3'd5;
localparam READ_DONE = 3'd6;
reg [RP_AWIDTH-1:0] addr;
reg [RP_DWIDTH-1:0] rb_data;
reg [RP_DWIDTH-1:0] wr_data;
reg [2:0] state;
reg [9:0] count;
reg [1:0] rresp;
reg [1:0] bresp;
always @ (posedge s_axi_aclk) begin
if (s_axi_areset) begin
state <= IDLE;
addr <= 'd0;
rb_data <= 'd0;
wr_data <= 'd0;
count <= 10'd0;
rresp <= 2'd0;
bresp <= 2'd0;
end
else case (state)
IDLE: begin
if (s_axi_arvalid) begin
state <= READ_INIT;
addr <= s_axi_araddr[RP_AWIDTH-1:0];
end
else if (s_axi_awvalid) begin
state <= WRITE_INIT;
addr <= s_axi_awaddr[RP_AWIDTH-1:0];
end
end
READ_INIT: begin
state <= READ_IN_PROGRESS;
count <= 10'd0;
rresp <= 2'b00;
end
READ_IN_PROGRESS: begin
if (reg_port_out_ready) begin
rb_data <= reg_port_out_data;
state <= READ_DONE;
end
else if (count >= TIMEOUT) begin
state <= READ_DONE;
rresp <= 2'b10;
end
else begin
count <= count + 1'b1;
end
end
READ_DONE: begin
if (s_axi_rready) begin
state <= IDLE;
end
end
WRITE_INIT: begin
if (s_axi_wvalid) begin
wr_data <= s_axi_wdata[RP_DWIDTH-1:0];
state <= WRITE_IN_PROGRESS;
count <= 10'd0;
bresp <= 2'b00;
end
end
WRITE_IN_PROGRESS: begin
if (reg_port_out_ready) begin
state <= WRITE_DONE;
end
else if (count >= TIMEOUT) begin
state <= READ_DONE;
bresp <= 2'b10;
end
else begin
count <= count + 1'b1;
end
end
WRITE_DONE: begin
if (s_axi_bready)
state <= IDLE;
end
default: begin
state <= IDLE;
end
endcase
end
assign s_axi_awready = (state == IDLE);
assign s_axi_wready = (state == WRITE_INIT);
assign s_axi_bvalid = (state == WRITE_DONE);
assign s_axi_bresp = bresp;
assign s_axi_arready = (state == IDLE);
assign s_axi_rdata = rb_data;
assign s_axi_rvalid = (state == READ_DONE);
assign s_axi_rresp = rresp;
assign reg_port_in_wt = (state == WRITE_INIT) & s_axi_wvalid;
assign reg_port_in_data = (state == WRITE_INIT) ? s_axi_wdata : wr_data;
assign reg_port_in_addr = addr;
assign reg_port_in_rd = (state == READ_INIT);
endmodule
+30
View File
@@ -0,0 +1,30 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
module bin2gray
#(parameter WIDTH=8)
(input [WIDTH-1:0] bin,
output [WIDTH-1:0] gray);
assign gray = (bin >> 1) ^ bin;
endmodule // bin2gray
+45
View File
@@ -0,0 +1,45 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`define log2(N) ( N < 2 ? 0 : \
N < 4 ? 1 : \
N < 8 ? 2 : \
N < 16 ? 3 : \
N < 32 ? 4 : \
N < 64 ? 5 : \
N < 128 ? 6 : \
N < 256 ? 7 : \
N < 512 ? 8 : \
N < 1024 ? 9 : \
10 \
)
module binary_encoder
#(
parameter SIZE = 16
)
(
input [SIZE-1:0] in,
output [`log2(SIZE)-1:0] out
);
genvar m,n;
generate
// Loop enough times to represent the total number of input bits as an encoded value
for (m = 0; m <= `log2(SIZE-1); m = m + 1) begin: expand_or_tree
wire [SIZE-1:0] encoding;
// Build enable mask by iterating through every input bit.
for (n = 0; n < SIZE ; n = n + 1) begin: encode_this_bit
assign encoding[n] = n[m];
end
// OR tree for this output bit with appropriate bits enabled.
assign out[m] = |(encoding & in);
end
endgenerate
endmodule // binary_encoder
+148
View File
@@ -0,0 +1,148 @@
//
// Copyright 2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module db_control #(
// Drive SPI core with input spi_clk instead of ce_clk. This is useful if ce_clk is very slow which
// would cause spi transactions to take a long time. WARNING: This adds a clock crossing FIFO!
parameter USE_SPI_CLK = 0,
parameter SR_BASE = 160,
parameter RB_BASE = 16,
parameter NUM_SPI_SEN = 8
)(
// Commands from Radio Core
input clk, input reset,
input set_stb, input [7:0] set_addr, input [31:0] set_data,
output reg rb_stb, input [7:0] rb_addr, output reg [63:0] rb_data,
input run_rx, input run_tx,
// Frontend / Daughterboard I/O
input [31:0] misc_ins, output [31:0] misc_outs,
input [31:0] fp_gpio_in, output [31:0] fp_gpio_out, output [31:0] fp_gpio_ddr, input [31:0] fp_gpio_fab,
input [31:0] db_gpio_in, output [31:0] db_gpio_out, output [31:0] db_gpio_ddr, input [31:0] db_gpio_fab,
output [31:0] leds,
input spi_clk, input spi_rst, output [NUM_SPI_SEN-1:0] sen, output sclk, output mosi, input miso
);
localparam [7:0] SR_MISC_OUTS = SR_BASE + 8'd0;
localparam [7:0] SR_SPI = SR_BASE + 8'd8;
localparam [7:0] SR_LEDS = SR_BASE + 8'd16;
localparam [7:0] SR_FP_GPIO = SR_BASE + 8'd24;
localparam [7:0] SR_DB_GPIO = SR_BASE + 8'd32;
localparam [7:0] RB_MISC_IO = RB_BASE + 0;
localparam [7:0] RB_SPI = RB_BASE + 1;
localparam [7:0] RB_LEDS = RB_BASE + 2;
localparam [7:0] RB_DB_GPIO = RB_BASE + 3;
localparam [7:0] RB_FP_GPIO = RB_BASE + 4;
/********************************************************
** Settings registers
********************************************************/
setting_reg #(.my_addr(SR_MISC_OUTS), .width(32)) sr_misc_outs (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out(misc_outs), .changed());
// Readback
reg spi_readback_stb_hold;
reg [31:0] spi_readback_hold;
wire [31:0] spi_readback_sync;
wire [31:0] fp_gpio_readback, db_gpio_readback;
always @* begin
case(rb_addr)
// Use a latched spi readback stobe so additional readbacks after a SPI transaction will work
RB_MISC_IO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {misc_ins, misc_outs}};
RB_SPI : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, spi_readback_hold}};
RB_LEDS : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, leds}};
RB_DB_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, db_gpio_readback}};
RB_FP_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, fp_gpio_readback}};
default : {rb_stb, rb_data} <= {spi_readback_stb_hold, {64'h0BADC0DE0BADC0DE}};
endcase
end
/********************************************************
** GPIO
********************************************************/
gpio_atr #(.BASE(SR_LEDS), .WIDTH(32), .FAB_CTRL_EN(0), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) leds_gpio_atr (
.clk(clk), .reset(reset),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.rx(run_rx), .tx(run_tx),
.gpio_in(32'd0), .gpio_out(leds), .gpio_ddr(/*unused, assumed output only*/),
.gpio_out_fab(32'h00000000 /*LEDs don't have fabric control*/), .gpio_sw_rb());
gpio_atr #(.BASE(SR_FP_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) fp_gpio_atr (
.clk(clk), .reset(reset),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.rx(run_rx), .tx(run_tx),
.gpio_in(fp_gpio_in), .gpio_out(fp_gpio_out), .gpio_ddr(fp_gpio_ddr),
.gpio_out_fab(fp_gpio_fab), .gpio_sw_rb(fp_gpio_readback));
gpio_atr #(.BASE(SR_DB_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) db_gpio_atr (
.clk(clk), .reset(reset),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.rx(run_rx), .tx(run_tx),
.gpio_in(db_gpio_in), .gpio_out(db_gpio_out), .gpio_ddr(db_gpio_ddr),
.gpio_out_fab(db_gpio_fab), .gpio_sw_rb(db_gpio_readback));
/********************************************************
** SPI
********************************************************/
wire spi_set_stb;
wire [7:0] spi_set_addr;
wire [31:0] spi_set_data;
wire spi_readback_stb, spi_readback_stb_sync;
wire [31:0] spi_readback;
wire spi_clk_int, spi_rst_int;
generate
if (USE_SPI_CLK) begin
axi_fifo_2clk #(.WIDTH(8 + 32), .SIZE(0)) set_2clk_i (
.reset(reset),
.i_aclk(clk), .i_tdata({set_addr, set_data}), .i_tvalid(set_stb), .i_tready(),
.o_aclk(spi_clk), .o_tdata({spi_set_addr, spi_set_data}), .o_tvalid(spi_set_stb), .o_tready(spi_set_stb));
axi_fifo_2clk #(.WIDTH(32), .SIZE(0)) rb_2clk_i (
.reset(reset),
.i_aclk(spi_clk), .i_tdata(spi_readback), .i_tvalid(spi_readback_stb), .i_tready(),
.o_aclk(clk), .o_tdata(spi_readback_sync), .o_tvalid(spi_readback_stb_sync), .o_tready(spi_readback_stb_sync));
assign spi_clk_int = spi_clk;
assign spi_rst_int = spi_rst;
end else begin
assign spi_set_stb = set_stb;
assign spi_set_addr = set_addr;
assign spi_set_data = set_data;
assign spi_readback_stb_sync = spi_readback_stb;
assign spi_readback_sync = spi_readback;
assign spi_clk_int = clk;
assign spi_rst_int = reset;
end
endgenerate
// Need to latch spi_readback_stb in case of additional readbacks
// after the initial spi transaction.
always @(posedge clk) begin
if (reset) begin
spi_readback_stb_hold <= 1'b1;
end else begin
if (set_stb & (set_addr == SR_SPI+2 /* Trigger address */)) begin
spi_readback_stb_hold <= 1'b0;
end else if (spi_readback_stb_sync) begin
spi_readback_hold <= spi_readback_sync;
spi_readback_stb_hold <= 1'b1;
end
end
end
// SPI Core instantiation
// Note: We don't use "ready" because we use readback_stb to backpressure the settings bus
simple_spi_core #(.BASE(SR_SPI), .WIDTH(NUM_SPI_SEN), .CLK_IDLE(0), .SEN_IDLE(8'hFF)) simple_spi_core (
.clock(spi_clk_int), .reset(spi_rst_int),
.set_stb(spi_set_stb), .set_addr(spi_set_addr), .set_data(spi_set_data),
.readback(spi_readback), .readback_stb(spi_readback_stb), .ready(/* Unused */),
.sen(sen), .sclk(sclk), .mosi(mosi), .miso(miso),
.debug());
endmodule
+70
View File
@@ -0,0 +1,70 @@
//
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: fe_control
//
// Description: Handle the front end control from the radio settings bus.
// The module gets generated NUM_CHANNELS times to give independent control to
// the individual channels.
//
module fe_control #(
parameter NUM_CHANNELS = 2,
parameter [7:0] SR_FE_CHAN_OFFSET = 16,
parameter [7:0] SR_TX_FE_BASE = 192,
parameter [7:0] SR_RX_FE_BASE = 200
)(
input clk, input reset,
// Commands from Radio Core
input set_stb, input [7:0] set_addr, input [31:0] set_data,
input time_sync,
// Radio datapath
input [NUM_CHANNELS-1:0] tx_stb, input [32*NUM_CHANNELS-1:0] tx_data_in, output [32*NUM_CHANNELS-1:0] tx_data_out,
output [NUM_CHANNELS-1:0] rx_stb, input [32*NUM_CHANNELS-1:0] rx_data_in, output [32*NUM_CHANNELS-1:0] rx_data_out
);
genvar i;
generate for (i = 0; i < NUM_CHANNELS; i = i + 1)
begin
localparam SR_TX_OFFSET_I = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
localparam SR_TX_OFFSET_Q = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
localparam SR_TX_MAG_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
localparam SR_TX_PHASE_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
localparam SR_TX_MUX = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
tx_frontend_gen3 #(
.SR_OFFSET_I(SR_TX_OFFSET_I), .SR_OFFSET_Q(SR_TX_OFFSET_Q),.SR_MAG_CORRECTION(SR_TX_MAG_CORRECTION),
.SR_PHASE_CORRECTION(SR_TX_PHASE_CORRECTION), .SR_MUX(SR_TX_MUX),
.BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0),
.DEVICE("7SERIES")
) tx_fe_corr_i (
.clk(clk), .reset(reset),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.tx_stb(tx_stb[i]), .tx_i(tx_data_in[32+(32*i)-1:16+(32*i)]), .tx_q(tx_data_in[16+(32*i)-1:(32*i)]),
.dac_stb(), .dac_i(tx_data_out[32+(32*i)-1:16+(32*i)]), .dac_q(tx_data_out[16+(32*i)-1:(32*i)])
);
localparam SR_RX_MAG_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
localparam SR_RX_PHASE_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
localparam SR_RX_OFFSET_I = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
localparam SR_RX_OFFSET_Q = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
localparam SR_RX_IQ_MAPPING = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
localparam SR_RX_HET_PHASE_INCR = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 5;
rx_frontend_gen3 #(
.SR_MAG_CORRECTION(SR_RX_MAG_CORRECTION), .SR_PHASE_CORRECTION(SR_RX_PHASE_CORRECTION), .SR_OFFSET_I(SR_RX_OFFSET_I),
.SR_OFFSET_Q(SR_RX_OFFSET_Q), .SR_IQ_MAPPING(SR_RX_IQ_MAPPING), .SR_HET_PHASE_INCR(SR_RX_HET_PHASE_INCR),
.BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_REALMODE_DSP(0),
.DEVICE("7SERIES")
) rx_fe_corr_i (
.clk(clk), .reset(reset), .sync_in(time_sync),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.adc_stb(1'b1), .adc_i(rx_data_in[32+(32*i)-1:16+(32*i)]), .adc_q(rx_data_in[16+(32*i)-1:(32*i)]),
.rx_stb(rx_stb[i]), .rx_i(rx_data_out[32+(32*i)-1:16+(32*i)]), .rx_q(rx_data_out[16+(32*i)-1:(32*i)])
);
end
endgenerate
endmodule
+78
View File
@@ -0,0 +1,78 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Discard silently packets which don't match this SID
module filter_bad_sid
(
input clk,
input reset,
input clear,
//
input [64:0] i_tdata,
input i_tvalid,
output i_tready,
//
output [64:0] o_tdata,
output o_tvalid,
input o_tready,
//
output reg [15:0] count
);
reg [1:0] state;
wire good_sid;
wire qualify_i_tvalid;
localparam IDLE = 0;
localparam ACCEPT = 1;
localparam DISCARD = 2;
always @(posedge clk)
if (reset | clear) begin
state <= IDLE;
count <= 0;
end else
case(state)
//
IDLE: begin
if (i_tvalid && i_tready)
if (good_sid)
state <= ACCEPT;
else begin
count <= count + 1;
state <= DISCARD;
end
end
//
ACCEPT: begin
if (i_tvalid && i_tready && i_tdata[64])
state <= IDLE;
end
//
DISCARD: begin
if (i_tvalid && i_tready && i_tdata[64])
state <= IDLE;
end
endcase // case(state)
assign good_sid = ((i_tdata[15:0] == 16'h00A0) || (i_tdata[15:0] == 16'h00B0));
assign qualify_i_tvalid = (state == IDLE) ? good_sid : ((state == DISCARD) ? 1'b0 : 1'b1);
//
// Buffer output, break combinatorial timing paths
//
axi_fifo_short #(.WIDTH(65)) fifo_short
(
.clk(clk), .reset(reset), .clear(clear),
.i_tdata(i_tdata), .i_tvalid(i_tvalid && qualify_i_tvalid), .i_tready(i_tready),
.o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
.space(), .occupied()
);
endmodule // filter_bad_sid
+98
View File
@@ -0,0 +1,98 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module gpio_atr #(
parameter BASE = 0,
parameter WIDTH = 32,
parameter FAB_CTRL_EN = 0,
parameter DEFAULT_DDR = 0,
parameter DEFAULT_IDLE = 0
) (
input clk, input reset, //Clock and reset
input set_stb, input [7:0] set_addr, input [31:0] set_data, //Settings control interface
input rx, input tx, //Run signals that indicate tx and rx operation
input [WIDTH-1:0] gpio_in, //GPIO input state
output reg [WIDTH-1:0] gpio_out, //GPIO output state
output reg [WIDTH-1:0] gpio_ddr, //GPIO direction (0=input, 1=output)
input [WIDTH-1:0] gpio_out_fab, //GPIO driver bus from fabric
output reg [WIDTH-1:0] gpio_sw_rb //Readback value for software
);
genvar i;
wire [WIDTH-1:0] in_idle, in_tx, in_rx, in_fdx, ddr_reg, atr_disable, fabric_ctrl;
reg [WIDTH-1:0] ogpio, igpio;
setting_reg #(.my_addr(BASE+0), .width(WIDTH), .at_reset(DEFAULT_IDLE)) reg_idle (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(in_idle),.changed());
setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(in_rx),.changed());
setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(in_tx),.changed());
setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(in_fdx),.changed());
setting_reg #(.my_addr(BASE+4), .width(WIDTH), .at_reset(DEFAULT_DDR)) reg_ddr (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(ddr_reg),.changed());
setting_reg #(.my_addr(BASE+5), .width(WIDTH)) reg_atr_disable (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(atr_disable),.changed());
generate if (FAB_CTRL_EN == 1) begin
setting_reg #(.my_addr(BASE+6), .width(WIDTH)) reg_fabric_ctrl (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
.out(fabric_ctrl),.changed());
end else begin
assign fabric_ctrl = {WIDTH{1'b0}};
end endgenerate
//Pipeline rx and tx signals for easier timing closure
reg rx_d, tx_d;
always @(posedge clk)
{rx_d, tx_d} <= {rx, tx};
generate for (i=0; i<WIDTH; i=i+1) begin: gpio_mux_gen
//ATR selection MUX
always @(posedge clk) begin
case({atr_disable[i], tx_d, rx_d})
3'b000: ogpio[i] <= in_idle[i];
3'b001: ogpio[i] <= in_rx[i];
3'b010: ogpio[i] <= in_tx[i];
3'b011: ogpio[i] <= in_fdx[i];
default: ogpio[i] <= in_idle[i]; //If ATR mode is disabled, always use IDLE value
endcase
end
//Pipeline input, output and direction
//For fabric access, insert MUX as close to the IO as possible
always @(posedge clk) begin
gpio_out[i] <= fabric_ctrl[i] ? gpio_out_fab[i] : ogpio[i];
end
end endgenerate
always @(posedge clk)
igpio <= gpio_in;
always @(posedge clk)
gpio_ddr <= ddr_reg;
//Generate software readback state
generate for (i=0; i<WIDTH; i=i+1) begin: gpio_rb_gen
always @(posedge clk)
gpio_sw_rb[i] <= gpio_ddr[i] ? gpio_out[i] : igpio[i];
end endgenerate
endmodule // gpio_atr
+38
View File
@@ -0,0 +1,38 @@
//
// Copyright 2015 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module gpio_atr_io #(
parameter WIDTH = 32
) (
input clk,
input [WIDTH-1:0] gpio_ddr,
input [WIDTH-1:0] gpio_out,
output [WIDTH-1:0] gpio_in,
inout [WIDTH-1:0] gpio_pins
);
//Instantiate registers in the IOB
(* IOB = "true" *) reg [WIDTH-1:0] gpio_in_iob, gpio_out_iob;
always @(posedge clk) begin
gpio_in_iob <= gpio_pins;
gpio_out_iob <= gpio_out;
end
assign gpio_in = gpio_in_iob;
//Pipeline the data direction bus
reg [WIDTH-1:0] gpio_ddr_reg;
always @(posedge clk)
gpio_ddr_reg <= gpio_ddr;
//Tristate buffers
genvar i;
generate for (i=0; i<WIDTH; i=i+1) begin: io_tristate_gen
assign gpio_pins[i] = gpio_ddr_reg[i] ? gpio_out_iob[i] : 1'bz;
end endgenerate
endmodule // gpio_atr_io
+25
View File
@@ -0,0 +1,25 @@
//
// Copyright 2016 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Gray: {a,b,c,d}
// Bits: {a,a^b,a^b^c,a^b^c^d}
//
module gray2bin #(
parameter WIDTH = 8)
(
input [WIDTH-1:0] gray,
output reg [WIDTH-1:0] bin
);
integer i;
always @(*) begin
bin[WIDTH-1] = gray[WIDTH-1];
for (i = WIDTH-2; i >= 0; i = i - 1) begin
bin[i] = bin[i+1] ^ gray[i];
end
end
endmodule
+3
View File
@@ -0,0 +1,3 @@
Contributions from:
Ettus Research, A National Instruments Company
Alex Forencich <alex@alexforencich.com>
+206
View File
@@ -0,0 +1,206 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_muxed_kv_map
//
// Description:
//
// This module implements a memory that stores key and value (KV) pairs such
// that the value can be looked up using the key (e.g., for a routing table).
// This implementation uses AXI stream for both inserting key-value pairs and
// for looking up a value by its key. It also supports multiple find/result
// AXI streams, which share the same KV map internally.
//
// Values are inserted into the KV map using the axis_insert_* AXI stream. A
// value can be looked up by its key using the axis_find_* AXI stream, in
// which case the resulting value is output on the axis_result_* AXI stream.
//
// Ports:
//
// axis_insert_tdest : Key to insert into the KV map
// axis_insert_tdata : Value to associate with the key in TDEST
// axis_insert_tvalid : Standard AXI stream TVALID
// axis_insert_tready : Standard AXI stream TREADY
//
// axis_find_tdata : Key to look up in the KV map
// axis_find_tvalid : Standard AXI stream TVALID
// axis_find_tready : Standard AXI stream TREADY
//
// axis_result_tdata : Value associated with key that was input on axis_find
// axis_result_tkeep : Indicates if TDATA contains a valid value (i.e.,
// TKEEP is 0 if the lookup fails to find a match)
// axis_result_tvalid : Standard AXI stream TVALID
// axis_result_tready : Standard AXI stream TREADY
//
// Parameters:
//
// KEY_WIDTH : Width of the key (axis_insert_tdest, axis_find_tdata)
// VAL_WIDTH : Width of the value (axis_insert_tdata, axis_result_tdata)
// SIZE : Size of the KV map (i.e., 2**SIZE key-value pairs)
// NUM_PORTS : Number of AXI-Stream ports for the find and result interfaces
//
module axis_muxed_kv_map #(
parameter KEY_WIDTH = 16,
parameter VAL_WIDTH = 32,
parameter SIZE = 6,
parameter NUM_PORTS = 4
) (
input wire clk,
input wire reset,
input wire [KEY_WIDTH-1:0] axis_insert_tdest,
input wire [VAL_WIDTH-1:0] axis_insert_tdata,
input wire axis_insert_tvalid,
output wire axis_insert_tready,
input wire [(KEY_WIDTH*NUM_PORTS)-1:0] axis_find_tdata,
input wire [NUM_PORTS-1:0] axis_find_tvalid,
output wire [NUM_PORTS-1:0] axis_find_tready,
output wire [(VAL_WIDTH*NUM_PORTS)-1:0] axis_result_tdata,
output wire [NUM_PORTS-1:0] axis_result_tkeep,
output wire [NUM_PORTS-1:0] axis_result_tvalid,
input wire [NUM_PORTS-1:0] axis_result_tready
);
localparam MUX_W = $clog2(NUM_PORTS) + KEY_WIDTH;
localparam DEMUX_W = $clog2(NUM_PORTS) + VAL_WIDTH + 1;
genvar i;
localparam [1:0] ST_IDLE = 2'd0;
localparam [1:0] ST_REQUEST = 2'd1;
localparam [1:0] ST_PENDING = 2'd2;
//---------------------------------------------------------
// Demux find ports
//---------------------------------------------------------
wire [KEY_WIDTH-1:0] find_key, find_key_reg;
wire find_key_stb;
wire [$clog2(NUM_PORTS)-1:0] find_dest, find_dest_reg;
wire find_key_valid, find_key_valid_reg;
wire find_ready;
reg find_in_progress = 1'b0;
wire insert_stb;
wire insert_busy;
wire find_res_stb;
wire [VAL_WIDTH-1:0] find_res_val;
wire find_res_match, find_res_ready;
wire [(MUX_W*NUM_PORTS)-1:0] mux_tdata;
generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_mux_input
assign mux_tdata[(MUX_W*i)+KEY_WIDTH-1:MUX_W*i] = axis_find_tdata[(KEY_WIDTH*i)+:KEY_WIDTH];
assign mux_tdata[(MUX_W*(i+1))-1:(MUX_W*i)+KEY_WIDTH] = i;
end endgenerate
axi_mux #(
.WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(NUM_PORTS),
.PRE_FIFO_SIZE(0), .POST_FIFO_SIZE($clog2(NUM_PORTS))
) mux_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata(mux_tdata), .i_tlast({NUM_PORTS{1'b1}}),
.i_tvalid(axis_find_tvalid), .i_tready(axis_find_tready),
.o_tdata({find_dest_reg, find_key_reg}), .o_tlast(),
.o_tvalid(find_key_valid_reg), .o_tready(find_ready)
);
axi_fifo #(
.WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(1)
) mux_reg_i (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({find_dest_reg, find_key_reg}),
.i_tvalid(find_key_valid_reg), .i_tready(find_ready),
.o_tdata({find_dest, find_key}),
.o_tvalid(find_key_valid), .o_tready(find_res_stb),
.space(), .occupied()
);
always @(posedge clk) begin
if (reset) begin
find_in_progress <= 1'b0;
end else begin
if (find_key_stb) begin
find_in_progress <= 1'b1;
end else if (find_res_stb) begin
find_in_progress <= 1'b0;
end
end
end
// find_key_stb indicates when to begin a new KV map lookup. We must wait
// until the output mux is ready before starting a lookup.
assign find_key_stb = find_key_valid & find_res_ready & ~find_in_progress;
//---------------------------------------------------------
// Insert logic
//---------------------------------------------------------
reg [1:0] ins_state = ST_IDLE;
always @(posedge clk) begin
if (reset) begin
ins_state <= ST_IDLE;
end else begin
case (ins_state)
ST_IDLE:
if (axis_insert_tvalid & ~insert_busy)
ins_state <= ST_REQUEST;
ST_REQUEST:
ins_state <= ST_PENDING;
ST_PENDING:
if (~insert_busy)
ins_state <= ST_IDLE;
default:
ins_state <= ST_IDLE;
endcase
end
end
assign axis_insert_tready = axis_insert_tvalid & (ins_state == ST_PENDING) & ~insert_busy;
assign insert_stb = axis_insert_tvalid & (ins_state == ST_REQUEST);
//---------------------------------------------------------
// KV map instantiation
//---------------------------------------------------------
kv_map #(
.KEY_WIDTH (KEY_WIDTH),
.VAL_WIDTH (VAL_WIDTH),
.SIZE (SIZE)
) map_i (
.clk (clk),
.reset (reset),
.insert_stb (insert_stb),
.insert_key (axis_insert_tdest),
.insert_val (axis_insert_tdata),
.insert_busy (insert_busy),
.find_key_stb (find_key_stb),
.find_key (find_key),
.find_res_stb (find_res_stb),
.find_res_match (find_res_match),
.find_res_val (find_res_val),
.count (/* unused */)
);
//---------------------------------------------------------
// Mux results port
//---------------------------------------------------------
wire [(DEMUX_W*NUM_PORTS)-1:0] demux_tdata;
wire [DEMUX_W-1:0] hdr;
axi_demux #(
.WIDTH(DEMUX_W), .SIZE(NUM_PORTS),
.PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(0)
) demux_i (
.clk(clk), .reset(reset), .clear(1'b0),
.header(hdr), .dest(hdr[DEMUX_W-1:VAL_WIDTH+1]),
.i_tdata({find_dest, find_res_match, find_res_val}), .i_tlast(1'b1),
.i_tvalid(find_res_stb), .i_tready(find_res_ready),
.o_tdata(demux_tdata), .o_tlast(),
.o_tvalid(axis_result_tvalid), .o_tready(axis_result_tready)
);
generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_result_output
assign axis_result_tdata[(VAL_WIDTH*i)+:VAL_WIDTH] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH-1:DEMUX_W*i];
assign axis_result_tkeep[i] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH];
end endgenerate
endmodule
+103
View File
@@ -0,0 +1,103 @@
/*
Copyright (c) 2015-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Content Addressable Memory
*/
module cam #(
// search data bus width
parameter DATA_WIDTH = 64,
// memory size in log2(words)
parameter ADDR_WIDTH = 5,
// CAM style (SRL, BRAM)
parameter CAM_STYLE = "SRL",
// width of data bus slices
parameter SLICE_WIDTH = 4
)
(
input wire clk,
input wire rst,
input wire [ADDR_WIDTH-1:0] write_addr,
input wire [DATA_WIDTH-1:0] write_data,
input wire write_delete,
input wire write_enable,
output wire write_busy,
input wire [DATA_WIDTH-1:0] compare_data,
output wire [2**ADDR_WIDTH-1:0] match_many,
output wire [2**ADDR_WIDTH-1:0] match_single,
output wire [ADDR_WIDTH-1:0] match_addr,
output wire match
);
generate
if (CAM_STYLE == "SRL") begin
cam_srl #(
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH),
.SLICE_WIDTH(SLICE_WIDTH)
)
cam_inst (
.clk(clk),
.rst(rst),
.write_addr(write_addr),
.write_data(write_data),
.write_delete(write_delete),
.write_enable(write_enable),
.write_busy(write_busy),
.compare_data(compare_data),
.match_many(match_many),
.match_single(match_single),
.match_addr(match_addr),
.match(match)
);
end else if (CAM_STYLE == "BRAM") begin
cam_bram #(
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH),
.SLICE_WIDTH(SLICE_WIDTH)
)
cam_inst (
.clk(clk),
.rst(rst),
.write_addr(write_addr),
.write_data(write_data),
.write_delete(write_delete),
.write_enable(write_enable),
.write_busy(write_busy),
.compare_data(compare_data),
.match_many(match_many),
.match_single(match_single),
.match_addr(match_addr),
.match(match)
);
end
endgenerate
endmodule
+259
View File
@@ -0,0 +1,259 @@
/*
Copyright (c) 2015-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Content Addressable Memory (block RAM based)
*/
module cam_bram #(
// search data bus width
parameter DATA_WIDTH = 64,
// memory size in log2(words)
parameter ADDR_WIDTH = 5,
// width of data bus slices
parameter SLICE_WIDTH = 9
)
(
input wire clk,
input wire rst,
input wire [ADDR_WIDTH-1:0] write_addr,
input wire [DATA_WIDTH-1:0] write_data,
input wire write_delete,
input wire write_enable,
output wire write_busy,
input wire [DATA_WIDTH-1:0] compare_data,
output wire [2**ADDR_WIDTH-1:0] match_many,
output wire [2**ADDR_WIDTH-1:0] match_single,
output wire [ADDR_WIDTH-1:0] match_addr,
output wire match
);
// total number of slices (enough to cover DATA_WIDTH with address inputs)
localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
// depth of RAMs
localparam RAM_DEPTH = 2**ADDR_WIDTH;
localparam [2:0]
STATE_INIT = 3'd0,
STATE_IDLE = 3'd1,
STATE_DELETE_1 = 3'd2,
STATE_DELETE_2 = 3'd3,
STATE_WRITE_1 = 3'd4,
STATE_WRITE_2 = 3'd5;
reg [2:0] state_reg = STATE_INIT, state_next;
wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
reg [SLICE_COUNT*SLICE_WIDTH-1:0] ram_addr = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
reg [RAM_DEPTH-1:0] set_bit;
reg [RAM_DEPTH-1:0] clear_bit;
reg wr_en;
reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
reg write_delete_reg = 1'b0, write_delete_next;
reg write_busy_reg = 1'b1;
assign write_busy = write_busy_reg;
reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
reg [RAM_DEPTH-1:0] match_many_raw;
assign match_many = match_many_raw;
reg [DATA_WIDTH-1:0] erase_ram [RAM_DEPTH-1:0];
reg [DATA_WIDTH-1:0] erase_data = {DATA_WIDTH{1'b0}};
reg erase_ram_wr_en;
integer i;
initial begin
for (i = 0; i < RAM_DEPTH; i = i + 1) begin
erase_ram[i] = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
end
end
integer k;
always @* begin
match_many_raw = {RAM_DEPTH{1'b1}};
for (k = 0; k < SLICE_COUNT; k = k + 1) begin
match_many_raw = match_many_raw & match_raw_out[k];
end
end
cam_priority_encoder #(
.WIDTH(RAM_DEPTH),
.LSB_PRIORITY("HIGH")
)
priority_encoder_inst (
.input_unencoded(match_many_raw),
.output_valid(match),
.output_encoded(match_addr),
.output_unencoded(match_single)
);
// BRAMs
genvar slice_ind;
generate
for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
localparam W = slice_ind == SLICE_COUNT-1 ? DATA_WIDTH-SLICE_WIDTH*slice_ind : SLICE_WIDTH;
wire [RAM_DEPTH-1:0] match_data;
wire [RAM_DEPTH-1:0] ram_data;
ram_2port #(
.DWIDTH(RAM_DEPTH),
.AWIDTH(W)
)
ram_inst
(
.clka(clk),
.ena(1'b1),
.wea(1'b0),
.addra(compare_data[SLICE_WIDTH * slice_ind +: W]),
.dia({RAM_DEPTH{1'b0}}),
.doa(match_data),
.clkb(clk),
.enb(1'b1),
.web(wr_en),
.addrb(ram_addr[SLICE_WIDTH * slice_ind +: W]),
.dib((ram_data & ~clear_bit) | set_bit),
.dob(ram_data)
);
always @* begin
match_raw_out[slice_ind] <= match_data;
end
end
endgenerate
// erase
always @(posedge clk) begin
erase_data <= erase_ram[write_addr_next];
if (erase_ram_wr_en) begin
erase_data <= write_data_padded_reg;
erase_ram[write_addr_next] <= write_data_padded_reg;
end
end
// write
always @* begin
state_next = STATE_IDLE;
count_next = count_reg;
ram_addr = erase_data;
set_bit = {RAM_DEPTH{1'b0}};
clear_bit = {RAM_DEPTH{1'b0}};
wr_en = 1'b0;
erase_ram_wr_en = 1'b0;
write_addr_next = write_addr_reg;
write_data_padded_next = write_data_padded_reg;
write_delete_next = write_delete_reg;
case (state_reg)
STATE_INIT: begin
// zero out RAMs
ram_addr = {SLICE_COUNT{count_reg}} & {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, {DATA_WIDTH{1'b1}}};
set_bit = {RAM_DEPTH{1'b0}};
clear_bit = {RAM_DEPTH{1'b1}};
wr_en = 1'b1;
if (count_reg == 0) begin
state_next = STATE_IDLE;
end else begin
count_next = count_reg - 1;
state_next = STATE_INIT;
end
end
STATE_IDLE: begin
// idle state
write_addr_next = write_addr;
write_data_padded_next = write_data_padded;
write_delete_next = write_delete;
if (write_enable) begin
// wait for read from erase_ram
state_next = STATE_DELETE_1;
end else begin
state_next = STATE_IDLE;
end
end
STATE_DELETE_1: begin
// wait for read
state_next = STATE_DELETE_2;
end
STATE_DELETE_2: begin
// clear bit and write back
clear_bit = 1'b1 << write_addr;
wr_en = 1'b1;
if (write_delete_reg) begin
state_next = STATE_IDLE;
end else begin
erase_ram_wr_en = 1'b1;
state_next = STATE_WRITE_1;
end
end
STATE_WRITE_1: begin
// wait for read
state_next = STATE_WRITE_2;
end
STATE_WRITE_2: begin
// set bit and write back
set_bit = 1'b1 << write_addr;
wr_en = 1'b1;
state_next = STATE_IDLE;
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_INIT;
count_reg <= {SLICE_WIDTH{1'b1}};
write_busy_reg <= 1'b1;
end else begin
state_reg <= state_next;
count_reg <= count_next;
write_busy_reg <= state_next != STATE_IDLE;
end
write_addr_reg <= write_addr_next;
write_data_padded_reg <= write_data_padded_next;
write_delete_reg <= write_delete_next;
end
endmodule
+94
View File
@@ -0,0 +1,94 @@
/*
Copyright (c) 2014-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Priority encoder module
*/
module cam_priority_encoder #
(
parameter WIDTH = 4,
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "LOW"
)
(
input wire [WIDTH-1:0] input_unencoded,
output wire output_valid,
output wire [$clog2(WIDTH)-1:0] output_encoded,
output wire [WIDTH-1:0] output_unencoded
);
// power-of-two width
parameter W1 = 2**$clog2(WIDTH);
parameter W2 = W1/2;
generate
if (WIDTH == 2) begin
// two inputs - just an OR gate
assign output_valid = |input_unencoded;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = input_unencoded[1];
end else begin
assign output_encoded = ~input_unencoded[0];
end
end else begin
// more than two inputs - split into two parts and recurse
// also pad input to correct power-of-two width
wire [$clog2(W2)-1:0] out1, out2;
wire valid1, valid2;
cam_priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst1 (
.input_unencoded(input_unencoded[W2-1:0]),
.output_valid(valid1),
.output_encoded(out1)
);
cam_priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst2 (
.input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
.output_valid(valid2),
.output_encoded(out2)
);
// multiplexer to select part
assign output_valid = valid1 | valid2;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
end else begin
assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
end
end
endgenerate
// unencoded output
assign output_unencoded = 1 << output_encoded;
endmodule
+223
View File
@@ -0,0 +1,223 @@
/*
Copyright (c) 2015-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Content Addressable Memory (shift register based)
*/
module cam_srl #(
// search data bus width
parameter DATA_WIDTH = 64,
// memory size in log2(words)
parameter ADDR_WIDTH = 5,
// width of data bus slices (4 for SRL16, 5 for SRL32)
parameter SLICE_WIDTH = 4
)
(
input wire clk,
input wire rst,
input wire [ADDR_WIDTH-1:0] write_addr,
input wire [DATA_WIDTH-1:0] write_data,
input wire write_delete,
input wire write_enable,
output wire write_busy,
input wire [DATA_WIDTH-1:0] compare_data,
output wire [2**ADDR_WIDTH-1:0] match_many,
output wire [2**ADDR_WIDTH-1:0] match_single,
output wire [ADDR_WIDTH-1:0] match_addr,
output wire match
);
// total number of slices (enough to cover DATA_WIDTH with address inputs)
localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
// depth of RAMs
localparam RAM_DEPTH = 2**ADDR_WIDTH;
localparam [1:0]
STATE_INIT = 2'd0,
STATE_IDLE = 2'd1,
STATE_WRITE = 2'd2,
STATE_DELETE = 2'd3;
reg [1:0] state_reg = STATE_INIT, state_next;
wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
reg [SLICE_COUNT-1:0] shift_data;
reg [RAM_DEPTH-1:0] shift_en;
reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
reg write_busy_reg = 1'b1;
assign write_busy = write_busy_reg;
reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
reg [RAM_DEPTH-1:0] match_many_raw;
reg [RAM_DEPTH-1:0] match_many_reg = {RAM_DEPTH{1'b0}};
assign match_many = match_many_reg;
integer k;
always @* begin
match_many_raw = ~shift_en;
for (k = 0; k < SLICE_COUNT; k = k + 1) begin
match_many_raw = match_many_raw & match_raw_out[k];
end
end
cam_priority_encoder #(
.WIDTH(RAM_DEPTH),
.LSB_PRIORITY("HIGH")
)
priority_encoder_inst (
.input_unencoded(match_many_reg),
.output_valid(match),
.output_encoded(match_addr),
.output_unencoded(match_single)
);
integer i;
// SRLs
genvar row_ind, slice_ind;
generate
for (row_ind = 0; row_ind < RAM_DEPTH; row_ind = row_ind + 1) begin : row
for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
reg [2**SLICE_WIDTH-1:0] srl_mem = {2**SLICE_WIDTH{1'b0}};
// match
always @* begin
match_raw_out[slice_ind][row_ind] = srl_mem[compare_data_padded[SLICE_WIDTH * slice_ind +: SLICE_WIDTH]];
end
// write
always @(posedge clk) begin
if (shift_en[row_ind]) begin
srl_mem <= {srl_mem[2**SLICE_WIDTH-2:0], shift_data[slice_ind]};
end
end
end
end
endgenerate
// match
always @(posedge clk) begin
match_many_reg <= match_many_raw;
end
// write
always @* begin
state_next = STATE_IDLE;
count_next = count_reg;
shift_data = {SLICE_COUNT{1'b0}};
shift_en = {RAM_DEPTH{1'b0}};
write_addr_next = write_addr_reg;
write_data_padded_next = write_data_padded_reg;
case (state_reg)
STATE_INIT: begin
// zero out shift registers
shift_en = {RAM_DEPTH{1'b1}};
shift_data = {SLICE_COUNT{1'b0}};
if (count_reg == 0) begin
state_next = STATE_IDLE;
end else begin
count_next = count_reg - 1;
state_next = STATE_INIT;
end
end
STATE_IDLE: begin
if (write_enable) begin
write_addr_next = write_addr;
write_data_padded_next = write_data_padded;
count_next = {SLICE_WIDTH{1'b1}};
if (write_delete) begin
state_next = STATE_DELETE;
end else begin
state_next = STATE_WRITE;
end
end else begin
state_next = STATE_IDLE;
end
end
STATE_WRITE: begin
// write entry
shift_en = 1'b1 << write_addr;
for (i = 0; i < SLICE_COUNT; i = i + 1) begin
shift_data[i] = count_reg == write_data_padded_reg[SLICE_WIDTH * i +: SLICE_WIDTH];
end
if (count_reg == 0) begin
state_next = STATE_IDLE;
end else begin
count_next = count_reg - 1;
state_next = STATE_WRITE;
end
end
STATE_DELETE: begin
// delete entry
shift_en = 1'b1 << write_addr;
shift_data = {SLICE_COUNT{1'b0}};
if (count_reg == 0) begin
state_next = STATE_IDLE;
end else begin
count_next = count_reg - 1;
state_next = STATE_DELETE;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_INIT;
count_reg <= {SLICE_WIDTH{1'b1}};
write_busy_reg <= 1'b1;
end else begin
state_reg <= state_next;
count_reg <= count_next;
write_busy_reg <= state_next != STATE_IDLE;
end
write_addr_reg <= write_addr_next;
write_data_padded_reg <= write_data_padded_next;
end
endmodule
+253
View File
@@ -0,0 +1,253 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: kv_map
module kv_map #(
parameter KEY_WIDTH = 16,
parameter VAL_WIDTH = 32,
parameter SIZE = 6
) (
// Clock and reset
input wire clk,
input wire reset,
// Insert port
input wire insert_stb,
input wire [KEY_WIDTH-1:0] insert_key,
input wire [VAL_WIDTH-1:0] insert_val,
output wire insert_busy,
// Find port
input wire find_key_stb,
input wire [KEY_WIDTH-1:0] find_key,
output wire find_res_stb,
output wire find_res_match,
output wire [VAL_WIDTH-1:0] find_res_val,
// Count
output reg [SIZE-1:0] count = {SIZE{1'b0}}
);
//-------------------------------------------------
// Instantiate a CAM and a RAM
//-------------------------------------------------
// The CAM serves as a "set" and the RAM serves as a
// random addressable "array". Using thse two data structures
// we can build a map. The role of the CAM is to compress
// the key to an address that can be used to lookup data
// stored in the RAM
wire cam_wr_en, cam_wr_busy, cam_rd_match;
wire [SIZE-1:0] cam_wr_addr, cam_rd_addr;
wire [KEY_WIDTH-1:0] cam_wr_data, cam_rd_key;
wire ram_wr_en;
wire [SIZE-1:0] ram_wr_addr;
reg [SIZE-1:0] ram_rd_addr;
wire [VAL_WIDTH-1:0] ram_wr_data, ram_rd_data;
cam #(
.DATA_WIDTH (KEY_WIDTH),
.ADDR_WIDTH (SIZE),
.CAM_STYLE (SIZE > 8 ? "BRAM" : "SRL"),
.SLICE_WIDTH (SIZE > 8 ? 9 : 5)
) cam_i (
.clk (clk),
.rst (reset),
.write_addr (cam_wr_addr),
.write_data (cam_wr_data),
.write_delete(1'b0),
.write_enable(cam_wr_en),
.write_busy (cam_wr_busy),
.compare_data(cam_rd_key),
.match_addr (cam_rd_addr),
.match (cam_rd_match),
.match_many (),
.match_single()
);
ram_2port #(
.DWIDTH(VAL_WIDTH),
.AWIDTH(SIZE)
) mem_i (
.clka (clk),
.ena (ram_wr_en),
.wea (1'b1),
.addra (ram_wr_addr),
.dia (ram_wr_data),
.doa (/* Write port only */),
.clkb (clk),
.enb (1'b1),
.web (1'b0),
.addrb (ram_rd_addr),
.dib (/* Read port only */),
.dob (ram_rd_data)
);
// Pipeline read address into RAM
always @(posedge clk)
ram_rd_addr <= cam_rd_addr;
//-------------------------------------------------
// Find state machine
//-------------------------------------------------
// The lookup process has three cycles of latency
// - CAM lookup has a 1 cycle latency
// - The lookup address into the RAM is delayed by 1 cycle for timing
// - The RAM takes 1 cycle to produce data
localparam FIND_CYC = 3;
reg [FIND_CYC-1:0] find_key_stb_shreg = {FIND_CYC{1'b0}};
reg [FIND_CYC-2:0] find_match_shreg = {(FIND_CYC-1){1'b0}};
reg find_pending = 1'b0;
wire find_busy = find_pending | find_key_stb;
// Delay the find valid signal to account for the latency
// of the CAM and RAM
always @(posedge clk) begin
find_key_stb_shreg <= reset ? {FIND_CYC{1'b0}} :
{find_key_stb_shreg[FIND_CYC-2:0], find_key_stb};
end
assign find_res_stb = find_key_stb_shreg[FIND_CYC-1];
// Latch the find signal to compute pending
always @(posedge clk) begin
if (find_key_stb)
find_pending <= 1'b1;
else if (find_pending)
find_pending <= ~find_res_stb;
end
// Delay the match signal to account for the latency of the RAM
always @(posedge clk) begin
find_match_shreg <= reset ? {(FIND_CYC-1){1'b0}} :
{find_match_shreg[FIND_CYC-3:0], cam_rd_match};
end
assign find_res_match = find_match_shreg[FIND_CYC-2];
//-------------------------------------------------
// Insert state machine
//-------------------------------------------------
localparam [2:0] ST_IDLE = 3'd0;
localparam [2:0] ST_WAIT_FIND = 3'd1;
localparam [2:0] ST_CAM_READ = 3'd2;
localparam [2:0] ST_CAM_CHECK_MATCH = 3'd3;
localparam [2:0] ST_CAM_RAM_WRITE = 3'd4;
localparam [2:0] ST_CAM_WRITE_WAIT = 3'd5;
localparam [2:0] ST_RAM_WRITE = 3'd6;
reg [2:0] ins_state = ST_IDLE;
reg [KEY_WIDTH-1:0] ins_key_cached;
reg [VAL_WIDTH-1:0] ins_val_cached;
reg [SIZE-1:0] write_addr = {SIZE{1'b0}};
reg [SIZE-1:0] next_addr = {SIZE{1'b0}};
always @(posedge clk) begin
if (reset) begin
ins_state <= ST_IDLE;
next_addr <= {SIZE{1'b0}};
end else begin
case (ins_state)
// Idle and waiting for an insert transaction
//
ST_IDLE: begin
// Cache insertion parameters
if (insert_stb) begin
ins_key_cached <= insert_key;
ins_val_cached <= insert_val;
// Wait for find to finish
ins_state <= find_busy ? ST_WAIT_FIND : ST_CAM_READ;
end
end
// Wait for a find transaction to finish
//
ST_WAIT_FIND: begin
// Wait for find to finish
if (~find_busy)
ins_state <= ST_CAM_READ;
end
// Read the CAM to check if the key to insert already exists
//
ST_CAM_READ: begin
// Ensure that find always has priority
if (~find_key_stb)
ins_state <= ST_CAM_CHECK_MATCH;
end
// Look at the CAM match signal to evaluate if we skip writing the CAM
//
ST_CAM_CHECK_MATCH: begin
// If the CAM already has this key, then overwrite it
if (cam_rd_match) begin
ins_state <= ST_RAM_WRITE;
write_addr <= cam_rd_addr;
end else if (~cam_wr_busy) begin
ins_state <= ST_CAM_RAM_WRITE;
write_addr <= next_addr;
next_addr <= next_addr + 1'b1;
end
end
// Write the specified key to the CAM and value to the RAM
//
ST_CAM_RAM_WRITE: begin
ins_state <= ST_CAM_WRITE_WAIT;
end
// Wait for CAM write to finish
//
ST_CAM_WRITE_WAIT: begin
if (~cam_wr_busy) begin
ins_state <= ST_IDLE;
count <= next_addr;
end
end
// Write the specified value to the RAM
//
ST_RAM_WRITE: begin
ins_state <= ST_IDLE;
count <= next_addr;
end
default: begin
// We should not get here
ins_state <= ST_IDLE;
end
endcase
end
end
// CAM Read Port:
// - Find has priority so it can interrupt an insert
assign cam_rd_key =
(ins_state != ST_CAM_READ || find_key_stb) ? find_key : ins_key_cached;
// RAM Write Port:
// - The RAM write enable is held high for 1 cycle
// - The address may come from a CAM lookup or could generated
assign ram_wr_en = (ins_state == ST_RAM_WRITE || ins_state == ST_CAM_RAM_WRITE);
assign ram_wr_addr = write_addr;
assign ram_wr_data = ins_val_cached;
// CAM Write Port:
// - The CAM write enable is held high for 1 cycle
// - The address may come from a CAM lookup or could generated (same as RAM)
assign cam_wr_en = (ins_state == ST_CAM_RAM_WRITE);
assign cam_wr_addr = write_addr;
assign cam_wr_data = ins_key_cached;
// Outputs
assign insert_busy = (ins_state != ST_IDLE);
assign find_res_val = ram_rd_data;
endmodule
+772
View File
@@ -0,0 +1,772 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module mdio_master #(
parameter REG_AWIDTH = 32,
parameter REG_BASE = 'h0,
parameter [7:0] MDC_DIVIDER = 8'd200
) (
// Clock and reset
input clk,
input rst,
// MDIO ports
output reg mdc,
output reg mdio_out,
output reg mdio_tri, // Assert to tristate driver.
input mdio_in,
// Register ports
input reg_wr_req,
input [REG_AWIDTH-1:0] reg_wr_addr,
input [31:0] reg_wr_data,
input reg_rd_req,
input [REG_AWIDTH-1:0] reg_rd_addr,
output reg reg_rd_resp,
output reg [31:0] reg_rd_data
);
localparam [7:0]
IDLE = 0,
PREAMBLE1 = 1,
PREAMBLE2 = 2,
PREAMBLE3 = 3,
PREAMBLE4 = 4,
PREAMBLE5 = 5,
PREAMBLE6 = 6,
PREAMBLE7 = 7,
PREAMBLE8 = 8,
PREAMBLE9 = 9,
PREAMBLE10 = 10,
PREAMBLE11 = 11,
PREAMBLE12 = 12,
PREAMBLE13 = 13,
PREAMBLE14 = 14,
PREAMBLE15 = 15,
PREAMBLE16 = 16,
PREAMBLE17 = 17,
PREAMBLE18 = 18,
PREAMBLE19 = 19,
PREAMBLE20 = 20,
PREAMBLE21 = 21,
PREAMBLE22 = 22,
PREAMBLE23 = 23,
PREAMBLE24 = 24,
PREAMBLE25 = 25,
PREAMBLE26 = 26,
PREAMBLE27 = 27,
PREAMBLE28 = 28,
PREAMBLE29 = 29,
PREAMBLE30 = 30,
PREAMBLE31 = 31,
PREAMBLE32 = 32,
START1 = 33,
C22_START2 = 34,
C45_START2 = 35,
OP1 = 36,
OP2 = 37,
PRTAD1 = 38,
PRTAD2 = 39,
PRTAD3 = 40,
PRTAD4 = 41,
PRTAD5 = 42,
DEVAD1 = 43,
DEVAD2 = 44,
DEVAD3 = 45,
DEVAD4 = 46,
DEVAD5 = 47,
TA1 = 48,
TA2 = 49,
TA3 = 50,
READ1 = 51,
READ2 = 52,
READ3 = 53,
READ4 = 54,
READ5 = 55,
READ6 = 56,
READ7 = 57,
READ8 = 58,
READ9 = 59,
READ10 = 60,
READ11 = 61,
READ12 = 62,
READ13 = 63,
READ14 = 64,
READ15 = 65,
READ16 = 66,
WRITE1 = 67,
WRITE2 = 68,
WRITE3 = 69,
WRITE4 = 70,
WRITE5 = 71,
WRITE6 = 72,
WRITE7 = 73,
WRITE8 = 74,
WRITE9 = 75,
WRITE10 = 76,
WRITE11 = 77,
WRITE12 = 78,
WRITE13 = 79,
WRITE14 = 80,
WRITE15 = 81,
WRITE16 = 82,
C45_ADDR1 = 83,
C45_ADDR2 = 84,
C45_ADDR3 = 85,
C45_ADDR4 = 86,
C45_ADDR5 = 87,
C45_ADDR6 = 88,
C45_ADDR7 = 89,
C45_ADDR8 = 90,
C45_ADDR9 = 91,
C45_ADDR10 = 92,
C45_ADDR11 = 93,
C45_ADDR12 = 94,
C45_ADDR13 = 95,
C45_ADDR14 = 96,
C45_ADDR15 = 97,
C45_ADDR16 = 98,
PREIDLE = 99
;
localparam REG_MDIO_DATA = REG_BASE + 'h0;
localparam REG_MDIO_ADDR = REG_BASE + 'h4;
localparam REG_MDIO_OP = REG_BASE + 'h8;
localparam REG_MDIO_CTRL_STATUS = REG_BASE + 'hC;
reg [15:0] mdio_read_data, mdio_write_data;
reg [15:0] mdio_address;
reg [12:0] mdio_operation;
reg [7:0] mdc_clk_count;
reg mdc_falling_edge;
reg mdio_running;
reg mdio_done;
reg [7:0] state;
always @(posedge clk) begin
if (rst) begin
mdio_write_data <= 16'h0;
mdio_address <= 16'h0;
mdio_operation <= 13'h0;
mdio_running <= 1'b0;
end else begin
// Handshake to MDIO state machine to reset running flag in status.
// Wait for falling MDC edge to prevent S/W race condition occuring
// where done flag still asserted but running flag now cleared (repeatedly).
if (mdio_done && mdc_falling_edge)
mdio_running <= 1'b0;
// Readable registers
if (reg_rd_req) begin
reg_rd_resp <= 1'b1;
case (reg_rd_addr)
REG_MDIO_DATA:
reg_rd_data <= {16'h0, mdio_read_data};
REG_MDIO_ADDR:
reg_rd_data <= {16'h0, mdio_address};
REG_MDIO_OP:
reg_rd_data <= {16'h0, mdio_operation};
REG_MDIO_CTRL_STATUS:
reg_rd_data <= {31'b0, mdio_running};
default:
reg_rd_resp <= 1'b0;
endcase
end else if (reg_rd_resp) begin
reg_rd_resp <= 1'b0;
end
// Writable registers
if (reg_wr_req) begin
case(reg_wr_addr)
REG_MDIO_DATA:
mdio_write_data <= reg_wr_data[15:0];
REG_MDIO_ADDR:
mdio_address <= reg_wr_data[15:0];
REG_MDIO_OP:
mdio_operation <= reg_wr_data[12:0];
REG_MDIO_CTRL_STATUS:
if (reg_wr_data[0])
mdio_running <= 1'b1; // Trigger mdio operation here. Cleared by state machine at end of bus transaction.
endcase
end
end
end
//
// Produce mdc clock as a signal synchronously from Wishbone clock.
//
always @(posedge clk) begin
if (rst) begin
mdc_clk_count <= 8'd1;
mdc <= 1'b0;
mdc_falling_edge <= 1'b0;
end else if (mdc_clk_count == (MDC_DIVIDER/2)) begin
mdc_clk_count <= 8'd1;
mdc <= ~mdc;
mdc_falling_edge <= mdc;
end else begin
mdc_clk_count <= mdc_clk_count + 8'd1;
mdc_falling_edge <= 1'b0;
end
end
//
// MDIO state machine
//
always @(posedge clk) begin
if (rst) begin
mdio_tri <= 1'b1;
mdio_out <= 1'b0;
mdio_done <= 1'b0;
mdio_read_data <= 16'b0;
state <= IDLE;
end else if (mdc_falling_edge) begin
// This is the MDIO bus controller. Use falling edge of MDC.
mdio_tri <= 1'b1;
mdio_out <= 1'b0;
mdio_done <= 1'b0;
case(state)
// IDLE.
// In Clause 22 & 45 the master of the MDIO bus is tristate during idle.
IDLE: begin
mdio_tri <= 1'b1;
mdio_out <= 1'b0;
if (mdio_running)
state <= PREAMBLE1;
end
// Preamble. All MDIO transactions begin witrh 32bits of 1 bits as a preamble.
PREAMBLE1: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE2;
end
PREAMBLE2: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE3;
end
PREAMBLE3: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE4;
end
PREAMBLE4: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE5;
end
PREAMBLE5: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE6;
end
PREAMBLE6: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE7;
end
PREAMBLE7: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE8;
end
PREAMBLE8: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE9;
end
PREAMBLE9: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE10;
end
PREAMBLE10: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE11;
end
PREAMBLE11: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE12;
end
PREAMBLE12: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE13;
end
PREAMBLE13: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE14;
end
PREAMBLE14: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE15;
end
PREAMBLE15: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE16;
end
PREAMBLE16: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE17;
end
PREAMBLE17: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE18;
end
PREAMBLE18: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE19;
end
PREAMBLE19: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE20;
end
PREAMBLE20: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE21;
end
PREAMBLE21: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE22;
end
PREAMBLE22: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE23;
end
PREAMBLE23: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE24;
end
PREAMBLE24: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE25;
end
PREAMBLE25: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE26;
end
PREAMBLE26: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE27;
end
PREAMBLE27: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE28;
end
PREAMBLE28: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE29;
end
PREAMBLE29: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE30;
end
PREAMBLE30: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE31;
end
PREAMBLE31: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= PREAMBLE32;
end
PREAMBLE32: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= START1;
end
// Start code for Clause 22 is 01 and Clause 45 is 00
START1: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b0;
if (mdio_operation[12])
// Clause 45 bit set.
state <= C45_START2;
else
state <= C22_START2;
end
// 2nd Clause 22 start bit is a 1
C22_START2: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= OP1;
end
// 2nd Clause 45 start bit is a 0
C45_START2: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b0;
state <= OP1;
end
// Both Clause 22 & 45 use 2 bits for operation and are compatable.
// Note we don't screen here for illegal Clause 22 ops.
OP1: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[11];
state <= OP2;
end
OP2: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[10];
state <= PRTAD1;
end
// Both Clause 22 & 45 use 2 sucsessive 5 bit fields to form a hierarchical address
// though it's used slightly different between the 2 standards.
PRTAD1: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[9];
state <= PRTAD2;
end
PRTAD2: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[8];
state <= PRTAD3;
end
PRTAD3: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[7];
state <= PRTAD4;
end
PRTAD4: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[6];
state <= PRTAD5;
end
PRTAD5: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[5];
state <= DEVAD1;
end
DEVAD1: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[4];
state <= DEVAD2;
end
DEVAD2: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[3];
state <= DEVAD3;
end
DEVAD3: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[2];
state <= DEVAD4;
end
DEVAD4: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[1];
state <= DEVAD5;
end
DEVAD5: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_operation[0];
state <= TA1;
end
// Both Clause 22 & Clause 45 use the same turn around on the bus.
// Reads have Z as the first bit and 0 driven by the slave for the 2nd bit.
// Note that slaves drive the bus on the rising edge of MDC.
// Writes and Address cycles have 10 driven by the master.
TA1: begin
if (mdio_operation[11] == 1'b0) // Write/Address
begin
mdio_tri <= 1'b0;
mdio_out <= 1'b1;
state <= TA2;
end
else // Read
begin
mdio_tri <= 1'b1;
state <= TA3;
end
end
TA2: begin
mdio_tri <= 1'b0;
mdio_out <= 1'b0;
if ( !mdio_operation[12]) // Clause 22 Write
state <= WRITE1;
else if (mdio_operation[10]) // Clause 45 Write
state <= WRITE1;
else // Clause 45 ADDRESS
state <= C45_ADDR1;
end
TA3: begin
mdio_tri <= 1'b1;
state <= READ1;
end
// Clause 22 Reads and both forms of clause 45 Reads have the same bus transaction from here out.
READ1: begin
mdio_tri <= 1'b1;
mdio_read_data[15] <= mdio_in;
state <= READ2;
end
READ2: begin
mdio_tri <= 1'b1;
mdio_read_data[14] <= mdio_in;
state <= READ3;
end
READ3: begin
mdio_tri <= 1'b1;
mdio_read_data[13] <= mdio_in;
state <= READ4;
end
READ4: begin
mdio_tri <= 1'b1;
mdio_read_data[12] <= mdio_in;
state <= READ5;
end
READ5: begin
mdio_tri <= 1'b1;
mdio_read_data[11] <= mdio_in;
state <= READ6;
end
READ6: begin
mdio_tri <= 1'b1;
mdio_read_data[10] <= mdio_in;
state <= READ7;
end
READ7: begin
mdio_tri <= 1'b1;
mdio_read_data[9] <= mdio_in;
state <= READ8;
end
READ8: begin
mdio_tri <= 1'b1;
mdio_read_data[8] <= mdio_in;
state <= READ9;
end
READ9: begin
mdio_tri <= 1'b1;
mdio_read_data[7] <= mdio_in;
state <= READ10;
end
READ10: begin
mdio_tri <= 1'b1;
mdio_read_data[6] <= mdio_in;
state <= READ11;
end
READ11: begin
mdio_tri <= 1'b1;
mdio_read_data[5] <= mdio_in;
state <= READ12;
end
READ12: begin
mdio_tri <= 1'b1;
mdio_read_data[4] <= mdio_in;
state <= READ13;
end
READ13: begin
mdio_tri <= 1'b1;
mdio_read_data[3] <= mdio_in;
state <= READ14;
end
READ14: begin
mdio_tri <= 1'b1;
mdio_read_data[2] <= mdio_in;
state <= READ15;
end
READ15: begin
mdio_tri <= 1'b1;
mdio_read_data[1] <= mdio_in;
state <= READ16;
end
READ16: begin
mdio_tri <= 1'b1;
mdio_read_data[0] <= mdio_in;
state <= PREIDLE;
mdio_done <= 1'b1;
end
// Write 16bits of data for all types of Write.
WRITE1: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[15];
state <= WRITE2;
end
WRITE2: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[14];
state <= WRITE3;
end
WRITE3: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[13];
state <= WRITE4;
end
WRITE4: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[12];
state <= WRITE5;
end
WRITE5: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[11];
state <= WRITE6;
end
WRITE6: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[10];
state <= WRITE7;
end
WRITE7: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[9];
state <= WRITE8;
end
WRITE8: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[8];
state <= WRITE9;
end
WRITE9: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[7];
state <= WRITE10;
end
WRITE10: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[6];
state <= WRITE11;
end
WRITE11: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[5];
state <= WRITE12;
end
WRITE12: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[4];
state <= WRITE13;
end
WRITE13: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[3];
state <= WRITE14;
end
WRITE14: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[2];
state <= WRITE15;
end
WRITE15: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[1];
state <= WRITE16;
end
WRITE16: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_write_data[0];
state <= PREIDLE;
mdio_done <= 1'b1;
end
// Write 16bits of address for a Clause 45 Address transaction
C45_ADDR1: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[15];
state <= C45_ADDR2;
end
C45_ADDR2: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[14];
state <= C45_ADDR3;
end
C45_ADDR3: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[13];
state <= C45_ADDR4;
end
C45_ADDR4: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[12];
state <= C45_ADDR5;
end
C45_ADDR5: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[11];
state <= C45_ADDR6;
end
C45_ADDR6: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[10];
state <= C45_ADDR7;
end
C45_ADDR7: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[9];
state <= C45_ADDR8;
end
C45_ADDR8: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[8];
state <= C45_ADDR9;
end
C45_ADDR9: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[7];
state <= C45_ADDR10;
end
C45_ADDR10: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[6];
state <= C45_ADDR11;
end
C45_ADDR11: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[5];
state <= C45_ADDR12;
end
C45_ADDR12: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[4];
state <= C45_ADDR13;
end
C45_ADDR13: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[3];
state <= C45_ADDR14;
end
C45_ADDR14: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[2];
state <= C45_ADDR15;
end
C45_ADDR15: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[1];
state <= C45_ADDR16;
end
C45_ADDR16: begin
mdio_tri <= 1'b0;
mdio_out <= mdio_address[0];
state <= PREIDLE;
mdio_done <= 1'b1;
end
// PREIDLE allows the mdio_running bit to reset.
PREIDLE: begin
state <= IDLE;
end
endcase // case(state)
end // if (mdc_falling_edge)
end
endmodule
+28
View File
@@ -0,0 +1,28 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module por_gen
(input clk,
output reset_out);
reg por_rst;
reg [7:0] por_counter = 8'h0;
always @(posedge clk)
if (por_counter != 8'h55)
begin
por_counter <= por_counter + 8'h1;
por_rst <= 1'b1;
end
else
por_rst <= 1'b0;
assign reset_out = por_rst;
endmodule // por_gen
+55
View File
@@ -0,0 +1,55 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`define log2(N) ( N < 2 ? 0 : \
N < 4 ? 1 : \
N < 8 ? 2 : \
N < 16 ? 3 : \
N < 32 ? 4 : \
N < 64 ? 5 : \
N < 128 ? 6 : \
N < 256 ? 7 : \
N < 512 ? 8 : \
N < 1024 ? 9 : \
10 \
)
module priority_encoder
#(
parameter WIDTH = 16
)
(
input [WIDTH-1:0] in,
output [`log2(WIDTH)-1:0] out
);
wire [WIDTH-1:0] one_hot;
// the priority encoder spits out the position
// of the leading bit as one hot coding
priority_encoder_one_hot #
(
.WIDTH(WIDTH)
)
prio_one_hot0
(
.in(in),
.out(one_hot)
);
// binary encoder turns the one hot coding
// into binary encoding
binary_encoder #
(
.WIDTH(WIDTH)
)
binary_enc0
(
.in(one_hot),
.out(out)
);
endmodule
+34
View File
@@ -0,0 +1,34 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module priority_encoder_one_hot
#(
parameter WIDTH = 16
)
(
input [WIDTH-1:0] in,
output [WIDTH-1:0] out
);
wire [WIDTH-1:0] in_rev;
wire [WIDTH-1:0] in_rev_inv_po = ~in_rev + 1;
wire [WIDTH-1:0] mask;
generate
genvar i,j;
for (i=0; i<WIDTH; i=i+1)
assign in_rev[i] = in[WIDTH-1-i];
for (j=0; j<WIDTH; j=j+1)
assign mask[j] = in_rev_inv_po[WIDTH-1-j];
endgenerate
assign out = in & mask;
endmodule
+79
View File
@@ -0,0 +1,79 @@
//
// Copyright 2017 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: pulse_stretch
//
// Description:
//
// Pulse stretcher. Takes any input pulse that is SCALE+2 clock cycles or
// less and outputs a pulse that is SCALE+1 clock cycles. However, if an
// input pulse is longer than SCALE+2 clock cycles then the output pulse
// repeats. If more than one pulse is input within SCALE+2 clock cycles then
// additional pulses will be ignored.
//
// Examples (SCALE = 2):
//
// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
//
//
// pulse _/‾‾‾\_______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________
//
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___________________
//
//
// pulse _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________________________
//
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\_______________________
//
//
// pulse _/‾‾‾\_______/‾‾‾\___/‾‾‾\_______/‾‾‾‾‾‾‾\_______________
//
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\___
// Parameters:
//
// SCALE : The number of clock cycles to add to a single cycle pulse. Or, the
// number of clock cycles, minus 1, for the output pulse.
//
module pulse_stretch #(
parameter SCALE = 64'd12_500_000
)(
input clk,
input rst,
input pulse,
output pulse_stretched
);
reg [$clog2(SCALE+1)-1:0] count = 'd0;
reg state = 1'b0;
always @ (posedge clk)
if (rst) begin
state <= 1'b0;
count <= 'd0;
end
else begin
case (state)
1'b0: begin
if (pulse) begin
state <= 1'b1;
count <= 'd0;
end
end
1'b1: begin
if (count == SCALE)
state <= 1'b0;
else
count <= count + 1'b1;
end
endcase
end
assign pulse_stretched = (state == 1'b1);
endmodule
+79
View File
@@ -0,0 +1,79 @@
//
// Copyright 2017 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: pulse_stretch_min
//
// Description:
//
// Pulse stretcher, to guarantee a minimum pulse width. Takes a short input
// pulse and outputs a pulse that is LENGTH clock cycles long. If the input
// pulse is longer than LENGTH then the output pulse will be the same length
// as the input pulse. The output is registered so the output is delayed by
// one clock cycle relative to the input. If more than one pulse is input
// within LENGTH+1 clock cycles, then the extra input pulses will not
// generate output pulses.
//
// Examples: (LENGTH = 3)
//
// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
//
//
// pulse_in _/‾‾‾\_______________/‾‾‾‾‾‾‾\___________/‾‾‾‾‾‾‾‾‾‾‾\_______
//
// pulse_out _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___
//
//
// pulse_in _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾\_______/‾‾‾\______
//
// pulse_out ______/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\__
//
//
// Parameters:
//
// LENGTH : Length of the minimum pulse to output, in clock cycles.
//
module pulse_stretch_min #(
parameter LENGTH = 4
) (
input wire clk,
input wire rst,
input wire pulse_in,
output reg pulse_out = 0
);
reg [$clog2(LENGTH)-1:0] count = 0;
reg state = 0;
always @ (posedge clk)
if (rst) begin
state <= 0;
count <= 0;
pulse_out <= 0;
end
else begin
case (state)
1'b0: begin
if (pulse_in) begin
state <= 1;
pulse_out <= 1;
count <= 0;
end
end
1'b1: begin
if (count == LENGTH-1) begin
if (!pulse_in) begin
state <= 0;
pulse_out <= 0;
end
end else
count <= count + 1;
end
endcase
end
endmodule
+70
View File
@@ -0,0 +1,70 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: pulse_synchronizer
// Description:
// - Synchronizes a single-cycle pulse or an edge from one
// clock domain to another
// - Clocks A and B can be asynchronous
//
module pulse_synchronizer #(
parameter MODE = "PULSE", // Capture mode {PULSE, POSEDGE, NEGEDGE}
parameter STAGES = 2 // Number of synchronizer stages
) (
input clk_a, // Clock A
input rst_a, // Reset in clock domain A
input pulse_a, // Pulse in clock domain A to synchronize
output busy_a, // Synchronizer is busy (pulse_a ignored when asserted)
input clk_b, // Clock B
output pulse_b // Pulse in clock domain B
);
// Trigger logic based on the capture mode
wire trigger;
generate if (MODE == "POSEDGE") begin
reg pulse_a_del_pe = 1'b0;
always @ (posedge clk_a)
pulse_a_del_pe <= rst_a ? 1'b0 : pulse_a;
assign trigger = pulse_a & ~pulse_a_del_pe;
end else if (MODE == "NEGEDGE") begin
reg pulse_a_del_ne = 1'b1;
always @ (posedge clk_a)
pulse_a_del_ne <= rst_a ? 1'b1 : pulse_a;
assign trigger = ~pulse_a & pulse_a_del_ne;
end else begin
assign trigger = pulse_a;
end endgenerate
// Translate pulse/edge to a level and synchronize that into the B domain
reg pulse_toggle_a = 1'b0;
always @(posedge clk_a) begin
pulse_toggle_a <= rst_a ? 1'b0 : (pulse_toggle_a ^ (trigger & ~busy_a));
end
wire pulse_toggle_b;
reg pulse_toggle_b_del = 1'b0;
wire handshake_toggle_a;
synchronizer #(
.STAGES(STAGES), .INITIAL_VAL(0)
) toggle_sync_i (
.clk(clk_b), .rst(1'b0), .in(pulse_toggle_a), .out(pulse_toggle_b)
);
// Handshake toggle signal back into the A domain to deassert busy
synchronizer #(
.STAGES(STAGES), .INITIAL_VAL(0)
) handshake_sync_i (
.clk(clk_a), .rst(1'b0), .in(pulse_toggle_b_del), .out(handshake_toggle_a)
);
always @(posedge clk_b) begin
pulse_toggle_b_del <= pulse_toggle_b;
end
assign pulse_b = pulse_toggle_b_del ^ pulse_toggle_b;
assign busy_a = pulse_toggle_a ^ handshake_toggle_a;
endmodule
+120
View File
@@ -0,0 +1,120 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Description
// This code implements a parameterizable true dual port memory
// (both ports can read and write). If an enable is not necessary
// it may be tied off.
//
// Note
// This module requires the ram_2port_impl.vh header file. The
// header is included multiple times with different values of
// the RAM_DIRECTIVE macro to create different implementations of the
// RAM. An implementation is chosen in ram_2port based on the
// user parameter for RAM_TYPE.
// Mode: AUTOMATIC
`define RAM_DIRECTIVE
`define RAM_MOD_NAME ram_2port_impl_auto
`include "ram_2port_impl.vh"
`undef RAM_MOD_NAME
`undef RAM_DIRECTIVE
// Mode: REG
`define RAM_DIRECTIVE (* ram_style = "registers" *)
`define RAM_MOD_NAME ram_2port_impl_reg
`include "ram_2port_impl.vh"
`undef RAM_MOD_NAME
`undef RAM_DIRECTIVE
// Mode: LUTRAM
`define RAM_DIRECTIVE (* ram_style = "distributed" *)
`define RAM_MOD_NAME ram_2port_impl_lutram
`include "ram_2port_impl.vh"
`undef RAM_MOD_NAME
`undef RAM_DIRECTIVE
// Mode: BRAM
`define RAM_DIRECTIVE (* ram_style = "block" *)
`define RAM_MOD_NAME ram_2port_impl_bram
`include "ram_2port_impl.vh"
`undef RAM_MOD_NAME
`undef RAM_DIRECTIVE
// Mode: URAM
`define RAM_DIRECTIVE (* ram_style = "ultra" *)
`define RAM_MOD_NAME ram_2port_impl_uram
`include "ram_2port_impl.vh"
`undef RAM_MOD_NAME
`undef RAM_DIRECTIVE
module ram_2port #(
parameter DWIDTH = 32, // Width of the memory block
parameter AWIDTH = 9, // log2 of the depth of the memory block
parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
parameter RAM_TYPE = "AUTOMATIC", // Type of RAM to infer {AUTOMATIC, REG, LUTRAM, BRAM, URAM}
parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
parameter INIT_FILE = "" // Optionally initialize memory with this file
) (
input wire clka,
input wire ena,
input wire wea,
input wire [AWIDTH-1:0] addra,
input wire [DWIDTH-1:0] dia,
output wire [DWIDTH-1:0] doa,
input wire clkb,
input wire enb,
input wire web,
input wire [AWIDTH-1:0] addrb,
input wire [DWIDTH-1:0] dib,
output wire [DWIDTH-1:0] dob
);
generate
if (RAM_TYPE == "URAM")
ram_2port_impl_uram #(
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
) impl (
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
);
else if (RAM_TYPE == "BRAM")
ram_2port_impl_bram #(
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
) impl (
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
);
else if (RAM_TYPE == "LUTRAM")
ram_2port_impl_lutram #(
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
) impl (
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
);
else if (RAM_TYPE == "REG")
ram_2port_impl_reg #(
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
) impl (
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
);
else
ram_2port_impl_auto #(
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
) impl (
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
);
endgenerate
endmodule
+129
View File
@@ -0,0 +1,129 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Used by ram_2port.v
// Requires `RAM_MOD_NAME and `RAM_DIRECTIVE to be defined
module `RAM_MOD_NAME #(
parameter DWIDTH = 32, // Width of the memory block
parameter AWIDTH = 9, // log2 of the depth of the memory block
parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
parameter INIT_FILE = "" // Optionally initialize memory with this file
) (
input wire clka,
input wire ena,
input wire wea,
input wire [AWIDTH-1:0] addra,
input wire [DWIDTH-1:0] dia,
output wire [DWIDTH-1:0] doa,
input wire clkb,
input wire enb,
input wire web,
input wire [AWIDTH-1:0] addrb,
input wire [DWIDTH-1:0] dib,
output wire [DWIDTH-1:0] dob
);
`RAM_DIRECTIVE reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
// Initialize ram to a specified file or to all zeros to match hardware
generate if (INIT_FILE != "") begin
initial
$readmemh(INIT_FILE, ram, 0, (1<<AWIDTH)-1);
end else begin
integer i;
initial
for (i = 0; i < (1<<AWIDTH); i = i + 1)
ram[i] = {DWIDTH{1'b0}};
end endgenerate
reg [DWIDTH-1:0] doa_r = 'h0, dob_r = 'h0;
generate if (OUT_REG == 1) begin
// A 2 clock cycle read latency with improve clock-to-out timing
reg [DWIDTH-1:0] doa_rr = 'h0, dob_rr = 'h0;
always @(posedge clka)
if (ena)
doa_rr <= doa_r;
always @(posedge clkb)
if (enb)
dob_rr <= dob_r;
assign doa = doa_rr;
assign dob = dob_rr;
end else begin
// A 1 clock cycle read latency at the cost of a longer clock-to-out timing
assign doa = doa_r;
assign dob = dob_r;
end endgenerate
generate if (RW_MODE == "READ-FIRST") begin
// When data is written, the prior memory contents at the write
// address are presented on the output port.
always @(posedge clka) begin
if (ena) begin
if (wea)
ram[addra] <= dia;
doa_r <= ram[addra];
end
end
always @(posedge clkb) begin
if (enb) begin
if (web)
ram[addrb] <= dib;
dob_r <= ram[addrb];
end
end
end else if (RW_MODE == "WRITE-FIRST") begin
// The data being written to the RAM also resides on the output port.
always @(posedge clka) begin
if (ena) begin
if (wea) begin
ram[addra] <= dia;
doa_r <= dia;
end else begin
doa_r <= ram[addra];
end
end
end
always @(posedge clkb) begin
if (enb) begin
if (web) begin
ram[addrb] <= dib;
dob_r <= dib;
end else begin
dob_r <= ram[addrb];
end
end
end
end else begin
// This is a no change RAM which retains the last read value on the output during writes
// which is the most power efficient mode.
always @(posedge clka) begin
if (ena) begin
if (wea)
ram[addra] <= dia;
else
doa_r <= ram[addra];
end
end
always @(posedge clkb) begin
if (enb) begin
if (web)
ram[addrb] <= dib;
else
dob_r <= ram[addrb];
end
end
end endgenerate
endmodule
+46
View File
@@ -0,0 +1,46 @@
//
// Copyright 2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module regport_resp_mux #(
parameter WIDTH = 32,
parameter NUM_SLAVES = 2
)(
input clk,
input reset,
input [NUM_SLAVES-1:0] sla_rd_resp,
input [(NUM_SLAVES*WIDTH)-1:0] sla_rd_data,
output reg mst_rd_resp,
output reg [WIDTH-1:0] mst_rd_data
);
// Treat sla_rd_resp as a one-hot bus.
// If multiple resp lines are asserted at the same time, then
// it is a violation of the register port protocol
wire [NUM_SLAVES-1:0] bit_options[0:WIDTH-1];
wire [WIDTH-1:0] data_out;
genvar i, b;
generate
for (b = 0; b < WIDTH; b = b + 1) begin
for (i = 0; i < NUM_SLAVES; i = i + 1) begin
assign bit_options[b][i] = sla_rd_data[(i*WIDTH)+b];
end
assign data_out[b] = |(bit_options[b] & sla_rd_resp);
end
endgenerate
always @(posedge clk) begin
mst_rd_data <= data_out;
if (reset)
mst_rd_resp <= 1'b0;
else
mst_rd_resp <= |(sla_rd_resp);
end
endmodule
+63
View File
@@ -0,0 +1,63 @@
/////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: regport_to_settingsbus
// Description:
// Converts regport write bus to the a setting bus
// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
// uses word addressing and hence the address needs to be shifted by
// to convert to set_addr.
//
/////////////////////////////////////////////////////////////////////
module regport_to_settingsbus #(
parameter BASE = 14'h0,
parameter END_ADDR = 14'h3FFF,
parameter DWIDTH = 32,
parameter AWIDTH = 14,
parameter SR_AWIDTH = 12,
// Dealign for settings bus by shifting by 2
parameter ADDRESSING = "WORD",
parameter SHIFT = $clog2(DWIDTH/8)
)(
input reset,
input clk,
input reg_wr_req,
input [AWIDTH-1:0] reg_wr_addr,
input [DWIDTH-1:0] reg_wr_data,
output reg set_stb,
output reg [SR_AWIDTH-1:0] set_addr,
output reg [DWIDTH-1:0] set_data
);
wire set_stb_int;
wire [DWIDTH-1:0] set_data_int;
wire [SR_AWIDTH-1:0] set_addr_base;
wire [SR_AWIDTH-1:0] set_addr_int;
// Strobe asserted only when address is between BASE and END ADDR
assign set_stb_int = reg_wr_req && (reg_wr_addr >= BASE) && (reg_wr_addr <= END_ADDR);
assign set_addr_base = reg_wr_addr - BASE;
// Shift by 2 in case of setting bus
assign set_addr_int = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, set_addr_base[SR_AWIDTH-1:SHIFT]}
: set_addr_base[SR_AWIDTH-1:0];
assign set_data_int = reg_wr_data;
// Adding a pipeline stage
always @(posedge clk) begin
if (reset) begin
set_stb <= 'b0;
set_addr <= 'h0;
set_data <= 'h0;
end else begin
set_stb <= set_stb_int;
set_addr <= set_addr_int;
set_data <= set_data_int;
end
end
endmodule // regport_to_settingsbus
+113
View File
@@ -0,0 +1,113 @@
/////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: regport_to_xbar_settingsbus
// Description:
// Converts regport to xbar setting bus.
// The module is designed only for the crossbar. The readback bus for the
// rfnoc crossbar reads from the same address as it writes to. Also
// there is an extra cycle delay in read data in the crossbar, which is
// why the rb_stb needs to be delayed by a cycle.
//
// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
// uses word addressing and hence the address needs to be shifted by
// to convert to set_addr.
//
/////////////////////////////////////////////////////////////////////
module regport_to_xbar_settingsbus #(
parameter BASE = 14'h0,
parameter END_ADDR = 14'h3FFF,
parameter DWIDTH = 32,
parameter AWIDTH = 14,
parameter SR_AWIDTH = 12,
// Dealign for settings bus by shifting by 2
parameter ADDRESSING = "WORD",
parameter SHIFT = $clog2(DWIDTH/8)
)(
input clk,
input reset,
input reg_wr_req,
input [AWIDTH-1:0] reg_wr_addr,
input [DWIDTH-1:0] reg_wr_data,
input reg_rd_req,
input [AWIDTH-1:0] reg_rd_addr,
output [DWIDTH-1:0] reg_rd_data,
output reg_rd_resp,
output set_stb,
output [SR_AWIDTH-1:0] set_addr,
output [DWIDTH-1:0] set_data,
output rb_stb,
output [SR_AWIDTH-1:0] rb_addr,
input [DWIDTH-1:0] rb_data
);
reg reg_rd_req_delay;
reg reg_rd_req_delay2;
wire [AWIDTH-1:0] set_addr_int;
reg [AWIDTH-1:0] rb_addr_int;
always @(posedge clk) begin
if (reset) begin
reg_rd_req_delay <= 1'b0;
reg_rd_req_delay2 <= 1'b0;
rb_addr_int <= 'd0;
end
else if (reg_rd_req) begin
rb_addr_int <= reg_rd_addr - BASE;
reg_rd_req_delay <= 1'b1;
end
else if (reg_rd_req_delay) begin
reg_rd_req_delay2 <= 1'b1;
reg_rd_req_delay <= 1'b0;
end
// Deassert after two clock cycles
else if (reg_rd_req_delay2) begin
reg_rd_req_delay <= 1'b0;
reg_rd_req_delay2 <= 1'b0;
rb_addr_int <= 'd0;
end
else begin
reg_rd_req_delay <= 1'b0;
reg_rd_req_delay2 <= 1'b0;
rb_addr_int <= 'd0;
end
end
// Write mode of settings bus
regport_to_settingsbus #(
.BASE(BASE),
.END_ADDR(END_ADDR),
.DWIDTH(DWIDTH),
.AWIDTH(AWIDTH),
.SR_AWIDTH(SR_AWIDTH),
.ADDRESSING(ADDRESSING)
) xbar_write_settings_bus (
.clk(clk),
.reset(reset),
.reg_wr_req(reg_wr_req),
.reg_wr_addr(reg_wr_addr),
.reg_wr_data(reg_wr_data),
.set_stb(set_stb),
.set_addr(set_addr),
.set_data(set_data)
);
assign rb_addr = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, rb_addr_int[SR_AWIDTH-1:SHIFT]}
: rb_addr_int[SR_AWIDTH-1:0];
// Strobe asserted two cycle after read request only when address is between BASE and END ADDR
// This is specific to the xbar as the xbar delays read data by an extra clock
// cycle to relax timing.
assign rb_stb = reg_rd_req_delay2 && (reg_rd_addr >= BASE) && (reg_rd_addr <= END_ADDR);
assign reg_rd_resp = rb_stb;
assign reg_rd_data = rb_data;
endmodule
+37
View File
@@ -0,0 +1,37 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018-2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// The purpose of this module is to synchronize a reset from one clock domain
// to another. The reset_in signal must be driven by a glitch-free source.
//
module reset_sync (
// clock for the output reset
input clk,
// glitch-free input reset
input reset_in,
// output reset in the clk domain
output reg reset_out);
wire reset_c;
synchronizer #(
// The input reset is async to the output clk domain... so timing should not be
// analyzed here!
.FALSE_PATH_TO_IN(1),
// Assert reset_out by default. When clk starts toggling the downstream logic will
// be in reset for at least 10 clk cycles. This allows the clock to settle (if needed)
// and the reset to propagate fully to all logic.
.INITIAL_VAL(1),
.STAGES(10)
) reset_double_sync (
.clk(clk), .rst(1'b0), .in(reset_in), .out(reset_c)
);
always @(posedge clk)
reset_out <= reset_c;
endmodule // reset_sync
+144
View File
@@ -0,0 +1,144 @@
//
// Copyright 2011-2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
// Refer to SelectMAP and ICAP docs in UG470
//
module s7_icap_wb
(
input clk,
input reset,
input cyc_i,
input stb_i,
input we_i,
output ack_o,
input [31:0] dat_i,
output [31:0] dat_o
);
reg rdwrb, csib;
reg [2:0] icap_state;
localparam ICAP_IDLE = 0;
localparam ICAP_WR0 = 1;
localparam ICAP_WR1 = 2;
localparam ICAP_RD0 = 3;
localparam ICAP_RD1 = 4;
localparam IDLE = 1'b1;
localparam ACTIVE = 1'b0;
localparam READ = 1'b1;
localparam WRITE = 1'b0;
always @(posedge clk)
if(reset) begin
rdwrb <= READ;
csib <= IDLE;
icap_state <= ICAP_IDLE;
end
else
case(icap_state)
//
// In IDLE state waiting for a READ or WRITE to be signalled from the WB bus.
// (In this state rdwrb can flip state without effect because ICAP is not selected)
//
ICAP_IDLE :
begin
if(stb_i & cyc_i) begin
if(we_i) begin
// Start WRITE, assert RDWR_B LOW whilst CSI_B remains HIGH.
rdwrb <= WRITE;
csib <= IDLE;
icap_state <= ICAP_WR0;
end else begin
// Start READ
rdwrb <= READ;
csib <= IDLE;
icap_state <= ICAP_RD0;
end
end else begin
// Stay IDLE
rdwrb <= READ;
csib <= IDLE;
icap_state <= ICAP_IDLE;
end
end // case: ICAP_IDLE
//
// First cycle of WRITE.
// Next cycle assert RDWR_B LOW and assert CSI_B LOW.
//
ICAP_WR0 : begin
rdwrb <= WRITE;
csib <= ACTIVE;
icap_state <= ICAP_WR1;
end
//
// Second cycle of WRITE.
// Next cycle assert RDWR_B LOW and assert CSI_B HIGH whilst transitioning to IDLE state
//
ICAP_WR1 : begin
rdwrb <= WRITE;
csib <= IDLE;
icap_state <= ICAP_IDLE;
end
//
// First cycle of READ.
// Next cycle assert RDWR_B HIGH and assert CSI_B LOW.
//
ICAP_RD0 : begin
rdwrb <= READ;
csib <= ACTIVE;
icap_state <= ICAP_WR1;
end
//
// Second cycle of READ.
// Next cycle assert RDWR_B HIGH and assert CSI_B HIGH whilst transitioning to IDLE state
//
ICAP_RD1 : begin
rdwrb <= READ;
csib <= IDLE;
icap_state <= ICAP_IDLE;
end
endcase // case (icap_state)
assign ack_o = (icap_state == ICAP_WR1) | (icap_state == ICAP_RD1);
//assign debug_out = {17'd0, BUSY, dat_i[7:0], ~CE, ICAPCLK, ~WRITE, icap_state};
ICAPE2 #(
.DEVICE_ID(32'h03651093),
.ICAP_WIDTH("X32"),
.SIM_CFG_FILE_NAME("NONE")
)
ICAPE2_inst (
.O(/*dat_o[31:0]*/),
.CLK(clk), // Rising edge referenced for both reads and writes.
.CSIB(csib), // CSIB = 0 to select ICAP
.I(dat_i[31:0]), // Bitswaped as per SELECTMAP (See UG470 page 40)
.RDWRB(rdwrb) // RDWB = 0 for WRITE, = 1 for READ
);
assign dat_0 = 32'h0;
endmodule // s3a_icap_wb
+126
View File
@@ -0,0 +1,126 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module serial_to_settings
(
input clk,
input reset,
// Serial signals (async)
input scl,
input sda,
// Settngs bus out
output reg set_stb,
output reg [7:0] set_addr,
output reg [31:0] set_data,
// Debug
output [31:0] debug
);
reg [2:0] state;
localparam SEARCH = 3'h0;
localparam ADDRESS = 3'h1;
localparam DATA = 3'h2;
localparam STOP1 = 3'h3;
localparam STOP2 = 3'h4;
reg scl_pre_reg, scl_reg, scl_reg2;
reg sda_pre_reg, sda_reg, sda_reg2;
reg [4:0] counter;
always @(posedge clk) begin
scl_reg2 <= scl_reg;
scl_reg <= scl_pre_reg;
scl_pre_reg <= scl;
sda_reg2 <= sda_reg;
sda_reg <= sda_pre_reg;
sda_pre_reg <= sda;
end
always @(posedge clk)
if (reset) begin
state <= SEARCH;
counter <= 0;
set_addr <= 0;
set_data <= 0;
set_stb <= 0;
end else begin
case(state)
//
// Search for I2C like start indication: SDA goes low whilst clock is high.
//
SEARCH: begin
set_stb <= 0;
// Look for START.
if (scl_reg && scl_reg2 && !sda_reg && sda_reg2) begin
state <= ADDRESS;
counter <= 0;
end
end
//
// Count 8 Address bits.
// Master changes SDA on falling edge of SCL, we sample on the rising edge.
//
ADDRESS: begin
if (scl_reg && !scl_reg2) begin
set_addr[7:0] <= {set_addr[6:0],sda_reg};
if (counter == 7) begin
state <= DATA;
counter <= 0;
end else
counter <= counter + 1;
end
end
//
// Count 32 data bits.
// Master changes SDA on falling edge of SCL, we sample on the rising edge.
//
DATA: begin
if (scl_reg && !scl_reg2) begin
set_data[31:0] <= {set_data[30:0],sda_reg};
if (counter == 31) begin
state <= STOP1;
counter <= 0;
end else
counter <= counter + 1;
end
end
//
// Looks for rising SCL edge before STOP bit.
//
STOP1: begin
if (scl_reg && !scl_reg2) begin
state <= STOP2;
end
end
//
// Looks for STOP bit
//
STOP2: begin
if (scl_reg && scl_reg2 && sda_reg && !sda_reg2) begin
state <= SEARCH;
counter <= 0;
set_stb <= 1;
end
end
endcase // case(state)
end // else: !if(reset)
assign debug =
{
counter[4:0],
state[2:0],
scl_reg,
sda_reg
};
endmodule // serial_to_settings
+86
View File
@@ -0,0 +1,86 @@
//
// Copyright 2014 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module serial_to_settings_tb();
reg clk;
reg reset;
wire scl;
wire sda;
wire set_stb;
wire [7:0] set_addr;
wire [31:0] set_data;
//
// These registers optionaly used
// to drive nets through procedural assignments in test bench.
// These drivers default to tri-stated.
//
reg scl_r;
reg sda_r;
assign scl = scl_r;
assign sda = sda_r;
initial
begin
scl_r <= 1'bz;
sda_r <= 1'bz;
end
serial_to_settings serial_to_settings_i
(
.clk(clk),
.reset(reset),
// Serial signals (async)
.scl(scl),
.sda(sda),
// Settngs bus out
.set_stb(set_stb),
.set_addr(set_addr),
.set_data(set_data)
);
// Nasty HAck to convert settings to wishbone crudely.
reg wb_stb;
wire wb_ack_o;
always @(posedge clk)
if (reset)
wb_stb <= 0;
else
wb_stb <= set_stb ? 1 : ((wb_ack_o) ? 0 : wb_stb);
simple_uart debug_uart
(
.clk_i(clk),
.rst_i(reset),
.we_i(wb_stb),
.stb_i(wb_stb),
.cyc_i(wb_stb),
.ack_o(wb_ack_o),
.adr_i(set_addr[2:0]),
.dat_i(set_data[31:0]),
.dat_o(),
.rx_int_o(),
.tx_int_o(),
.tx_o(txd),
.rx_i(rxd),
.baud_o()
);
//
// Bring in a simulation script here
//
`include "simulation_script.v"
endmodule
+38
View File
@@ -0,0 +1,38 @@
//
// Copyright 2011-2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//----------------------------------------------------------------------
//-- A settings register is a peripheral for the settings register bus.
//-- When the settings register sees strobe abd a matching address,
//-- the outputs will be become registered to the given input bus.
//----------------------------------------------------------------------
module setting_reg
#(parameter my_addr = 0,
parameter awidth = 8,
parameter width = 32,
parameter at_reset=0)
(input clk, input rst, input strobe, input wire [awidth-1:0] addr,
input wire [31:0] in, output reg [width-1:0] out, output reg changed);
always @(posedge clk)
if(rst)
begin
out <= at_reset;
changed <= 1'b0;
end
else
if(strobe & (my_addr==addr))
begin
out <= in[width-1:0];
changed <= 1'b1;
end
else
changed <= 1'b0;
endmodule // setting_reg
+44
View File
@@ -0,0 +1,44 @@
//
// Copyright 2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Mux multiple settings buses
module settings_bus_mux #(
parameter PRIO=0, // 0 = Round robin, 1 = Lower ports get priority (see axi_mux)
parameter AWIDTH=8,
parameter DWIDTH=32,
parameter FIFO_SIZE=1,
parameter NUM_BUSES=2)
(
input clk, input reset, input clear,
input [NUM_BUSES-1:0] in_set_stb, input [NUM_BUSES*AWIDTH-1:0] in_set_addr, input [NUM_BUSES*DWIDTH-1:0] in_set_data,
output out_set_stb, output [AWIDTH-1:0] out_set_addr, output [DWIDTH-1:0] out_set_data, input ready
);
wire [NUM_BUSES*(AWIDTH+DWIDTH)-1:0] i_tdata;
generate
if(NUM_BUSES <= 1) begin
assign out_set_stb = in_set_stb;
assign out_set_addr = in_set_addr;
assign out_set_data = in_set_data;
end else begin
genvar i;
for (i = 0; i < NUM_BUSES; i = i + 1) begin
assign i_tdata[(i+1)*(AWIDTH+DWIDTH)-1:i*(AWIDTH+DWIDTH)] = {in_set_addr[(i+1)*AWIDTH-1:i*AWIDTH],in_set_data[(i+1)*DWIDTH-1:i*DWIDTH]};
end
axi_mux #(
.PRIO(PRIO),
.WIDTH(AWIDTH+DWIDTH),
.PRE_FIFO_SIZE($clog2(NUM_BUSES)),
.POST_FIFO_SIZE(FIFO_SIZE),
.SIZE(NUM_BUSES))
axi_mux (
.clk(clk), .reset(reset), .clear(clear),
.i_tdata(i_tdata), .i_tlast({NUM_BUSES{1'b1}}), .i_tvalid(in_set_stb), .i_tready(),
.o_tdata({out_set_addr,out_set_data}), .o_tlast(), .o_tvalid(out_set_stb), .o_tready(ready));
end
endgenerate
endmodule
+170
View File
@@ -0,0 +1,170 @@
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: settings_bus_timed_2clk
// Description:
// - Stores settings bus transaction in a FIFO and
// releases them based on VITA time input
// - Also moves the settings bus to the timebase
// clock domain
//
module settings_bus_timed_2clk #(
parameter SR_AWIDTH = 8,
parameter SR_DWIDTH = 32,
parameter RB_AWIDTH = 8,
parameter RB_DWIDTH = 64,
parameter TIMED_CMDS_EN = 0
) (
input sb_clk, // Settings bus clock
input sb_rst, // Reset (sb_clk)
input tb_clk, // Timebase clock
input tb_rst, // Reset (tb_clk)
input [63:0] vita_time, // Current timebase time
input s_set_stb, // Settings bus strobe
input [SR_AWIDTH-1:0] s_set_addr, // Settings address
input [SR_DWIDTH-1:0] s_set_data, // Settings data
input s_set_has_time, // Is this a timed command?
input [63:0] s_set_time, // Command time
output s_set_pending, // Is settings transaction pending?
input [RB_AWIDTH-1:0] s_rb_addr, // Readback address
output s_rb_stb, // Readback data strobe
output [RB_DWIDTH-1:0] s_rb_data, // Readback data value
output m_set_stb, // Settings bus strobe
output [SR_AWIDTH-1:0] m_set_addr, // Settings address
output [SR_DWIDTH-1:0] m_set_data, // Settings data
output m_set_has_time, // Is this a timed command?
output [63:0] m_set_time, // Command time
input m_set_pending, // Is settings transaction pending?
output [RB_AWIDTH-1:0] m_rb_addr, // Readback address
input m_rb_stb, // Readback data strobe
input [RB_DWIDTH-1:0] m_rb_data // Readback data value
);
// States for input and output state machines
localparam [2:0] ST_IDLE = 3'd0; // Nothing is happening on the bus
localparam [2:0] ST_SET_ISSUED = 3'd1; // A settings transaction has been issued
localparam [2:0] ST_SET_PENDING = 3'd2; // A settings transaction is pending
localparam [2:0] ST_RB_PENDING = 3'd3; // Waiting for readback data
localparam [2:0] ST_RB_DONE = 3'd4; // Readback data is valid
wire rb_valid;
// Input state machine
reg [2:0] in_state = ST_IDLE;
always @(posedge sb_clk) begin
if (sb_rst) begin
in_state <= ST_IDLE;
end else begin
case (in_state)
ST_IDLE: begin
if (s_set_stb) begin
in_state <= ST_SET_PENDING;
end
end
ST_SET_PENDING: begin
if (rb_valid) begin
in_state <= ST_RB_DONE;
end
end
ST_RB_DONE: begin
in_state <= ST_IDLE;
end
default: begin
in_state <= ST_IDLE;
end
endcase
end
end
assign s_set_pending = (in_state == ST_SET_PENDING);
assign s_rb_stb = (in_state == ST_RB_DONE);
// Clock crossing FIFO (settings)
// TODO: Look into a more efficient implementation for a single element
// clock crossing FIFO.
wire set_pending, set_finished;
axi_fifo_2clk #(
.WIDTH(SR_AWIDTH+SR_DWIDTH+1+64+RB_AWIDTH), .SIZE(0)
) sb_2clk_fifo_i (
.i_aclk(sb_clk), .reset(sb_rst),
.i_tdata({s_set_addr, s_set_data, s_set_has_time, s_set_time, s_rb_addr}),
.i_tvalid(s_set_stb), .i_tready(/* Ignored: FIFO may not have an exact size*/),
.o_aclk(tb_clk),
.o_tdata({m_set_addr, m_set_data, m_set_has_time, m_set_time, m_rb_addr}),
.o_tvalid(set_pending), .o_tready(set_finished)
);
// Time compare logic
// If ~has_time then pass the transaction through, otherwise wait for time
// to tick up to command time
wire now, late;
wire go = ((TIMED_CMDS_EN == 1) && m_set_has_time) ? (now | late) : 1'b1;
// If this is a timed command then vita_time == m_set_time one cycle before
// strobe is asserted i.e. timed strobe assertion has a one cycle latency
time_compare time_compare (
.clk(tb_clk), .reset(tb_rst),
.time_now(vita_time), .trigger_time(m_set_time),
.now(now), .early(), .late(late), .too_early()
);
// Clock crossing FIFO (readback)
reg [RB_DWIDTH-1:0] cached_rb_data;
axi_fifo_2clk #(
.WIDTH(RB_DWIDTH), .SIZE(0)
) rbdata_2clk_fifo_i (
.reset(tb_rst),
.i_aclk(tb_clk), .i_tdata(cached_rb_data), .i_tvalid(set_finished), .i_tready(),
.o_aclk(sb_clk), .o_tdata(s_rb_data), .o_tvalid(rb_valid), .o_tready(s_rb_stb)
);
// Output state machine
reg [2:0] out_state = ST_IDLE;
always @(posedge tb_clk) begin
if (tb_rst) begin
out_state <= ST_IDLE;
end else begin
case (out_state)
ST_IDLE: begin
if (go & set_pending) begin
out_state <= ST_SET_ISSUED;
end
end
ST_SET_ISSUED: begin
out_state <= ST_SET_PENDING;
end
ST_SET_PENDING: begin
if (~m_set_pending) begin
if (m_rb_stb) begin
out_state <= ST_RB_DONE;
cached_rb_data <= m_rb_data;
end else begin
out_state <= ST_RB_PENDING;
end
end
end
ST_RB_PENDING: begin
if (m_rb_stb) begin
out_state <= ST_RB_DONE;
cached_rb_data <= m_rb_data;
end
end
ST_RB_DONE: begin
out_state <= ST_IDLE;
end
default: begin
out_state <= ST_IDLE;
end
endcase
end
end
assign m_set_stb = (out_state == ST_SET_ISSUED);
assign set_finished = (out_state == ST_RB_DONE);
endmodule
+107
View File
@@ -0,0 +1,107 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Simple I2C core
// Settings reg map:
//
// BASE+0 control register
// byte0 - control bits, data byte, or command bits, prescaler
// byte1 - what to do? (documented in cpp file)
// write prescaler lo
// write prescaler hi
// write control
// write data
// write command
// read data
// read status
//
// Readback:
//
// byte0 has readback value based on the last read command
//
module simple_i2c_core
#(
//settings register base address
parameter BASE = 0,
//i2c line level at reset
parameter ARST_LVL = 1
)
(
//clock and synchronous reset
input clock, input reset,
//32-bit settings bus inputs
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//32-bit data readback
output reg [31:0] readback,
//read is high when i2c core can begin another transaction
output reg ready,
// I2C signals
// i2c clock line
input scl_pad_i, // SCL-line input
output scl_pad_o, // SCL-line output (always 1'b0)
output scl_padoen_o, // SCL-line output enable (active low)
// i2c data line
input sda_pad_i, // SDA-line input
output sda_pad_o, // SDA-line output (always 1'b0)
output sda_padoen_o, // SDA-line output enable (active low)
//optional debug output
output [31:0] debug
);
//declare command settings register
wire [7:0] sr_what, sr_data;
wire sr_changed;
setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out({sr_what, sr_data}),.changed(sr_changed));
//declare wb interface signals
wire [2:0] wb_addr;
wire [7:0] wb_data_mosi;
wire [7:0] wb_data_miso;
wire wb_we, wb_stb, wb_cyc;
wire wb_ack;
//create wishbone-based i2c core
i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
(.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
.wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
.wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
.wb_ack_o(wb_ack),.wb_inta_o(),
.scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
.sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
//not ready between setting register and wishbone ack
always @(posedge clock) begin
if (reset || wb_ack) ready <= 1;
else if (sr_changed) ready <= 0;
end
//register wishbone data on every ack
always @(posedge clock) begin
if (wb_ack) readback <= {24'b0, wb_data_miso};
end
//assign wishbone signals
assign wb_addr = sr_what[2:0];
assign wb_stb = sr_changed;
assign wb_we = wb_stb && sr_what[3];
assign wb_cyc = wb_stb;
assign wb_data_mosi = sr_data;
endmodule //simple_i2c_core
+229
View File
@@ -0,0 +1,229 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Simple SPI core, the simplest, yet complete spi core I can think of
// Settings register controlled.
// 2 settings regs, control and data
// 1 32-bit readback and status signal
// Settings reg map:
//
// BASE+0 divider setting
// bits [15:0] spi clock divider
//
// BASE+1 configuration input
// bits [23:0] slave select, bit0 = slave0 enabled
// bits [29:24] num bits (1 through 32)
// bit [30] data input edge = in data bit latched on rising edge of clock
// bit [31] data output edge = out data bit latched on rising edge of clock
//
// BASE+2 input data
// Writing this register begins a spi transaction.
// Bits are latched out from bit 0.
// Therefore, load this register in reverse.
//
// Readback
// Bits are latched into bit 0.
// Therefore, data will be in-order.
module simple_spi_core
#(
//settings register base address
parameter BASE = 0,
//width of serial enables (up to 24 is possible)
parameter WIDTH = 8,
//idle state of the spi clock
parameter CLK_IDLE = 0,
//idle state of the serial enables
parameter SEN_IDLE = 24'hffffff
)
(
//clock and synchronous reset
input clock, input reset,
//32-bit settings bus inputs
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//32-bit data readback
output [31:0] readback,
output reg readback_stb,
//read is high when spi core can begin another transaction
output ready,
//spi interface, slave selects, clock, data in, data out
output [WIDTH-1:0] sen,
output sclk,
output reg mosi,
input miso,
//optional debug output
output [31:0] debug
);
wire [15:0] sclk_divider;
setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(sclk_divider),.changed());
wire [23:0] slave_select;
wire [5:0] num_bits;
wire datain_edge, dataout_edge;
setting_reg #(.my_addr(BASE+1),.width(32)) ctrl_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
wire [31:0] mosi_data;
wire trigger_spi;
setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(mosi_data),.changed(trigger_spi));
localparam WAIT_TRIG = 0;
localparam PRE_IDLE = 1;
localparam CLK_REG = 2;
localparam CLK_INV = 3;
localparam POST_IDLE = 4;
localparam IDLE_SEN = 5;
reg [2:0] state;
reg ready_reg;
assign ready = ready_reg && ~trigger_spi;
//serial clock either idles or is in one of two clock states
reg sclk_reg;
assign sclk = sclk_reg;
//serial enables either idle or enabled based on state
// IJB. One pipeline stage to break critical path from register in I/O pads.
wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
reg [WIDTH-1:0] sen_reg = SEN_IDLE;
always @(posedge clock) begin
if (reset) begin
sen_reg <= SEN_IDLE;
end else begin
sen_reg <= sen24[WIDTH-1:0];
end
end
assign sen = sen_reg;
//data output shift register
// IJB. One pipeline stage to break critical path from register in I/O pads.
reg [31:0] dataout_reg;
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
always @(posedge clock)
mosi <= dataout_reg[31];
//data input shift register
// IJB. Two pipeline stages to break critical path from register in I/O pads.
reg miso_pipe, miso_pipe2;
always @(posedge clock) begin
miso_pipe2 <= miso;
miso_pipe <= miso_pipe2;
end
reg [31:0] datain_reg;
wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
assign readback = datain_reg;
//counter for spi clock
reg [15:0] sclk_counter;
wire sclk_counter_done = (sclk_counter == sclk_divider);
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
//counter for latching bits miso/mosi
reg [6:0] bit_counter;
wire [6:0] bit_counter_next = bit_counter + 1;
wire bit_counter_done = (bit_counter_next == num_bits);
always @(posedge clock) begin
if (reset) begin
state <= WAIT_TRIG;
sclk_reg <= CLK_IDLE;
ready_reg <= 0;
readback_stb <= 1'b0;
end
else begin
case (state)
WAIT_TRIG: begin
if (trigger_spi) state <= PRE_IDLE;
readback_stb <= 1'b0;
ready_reg <= ~trigger_spi;
dataout_reg <= mosi_data;
sclk_counter <= 0;
bit_counter <= 0;
sclk_reg <= CLK_IDLE;
end
PRE_IDLE: begin
if (sclk_counter_done) state <= CLK_REG;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
CLK_REG: begin
if (sclk_counter_done) begin
state <= CLK_INV;
if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
CLK_INV: begin
if (sclk_counter_done) begin
state <= (bit_counter_done)? POST_IDLE : CLK_REG;
bit_counter <= bit_counter_next;
if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
POST_IDLE: begin
if (sclk_counter_done) state <= IDLE_SEN;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
IDLE_SEN: begin
if (sclk_counter_done) begin
ready_reg <= 1'b1;
readback_stb <= 1'b1;
state <= WAIT_TRIG;
end
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
default: state <= WAIT_TRIG;
endcase //state
end
end
assign debug = {
trigger_spi, state, //4
sclk, mosi, miso, ready, //4
//sen[7:0], //8
1'b0, bit_counter[6:0], //8
sclk_counter_done, bit_counter_done, //2
sclk_counter[5:0] //6
};
endmodule //simple_spi_core
+50
View File
@@ -0,0 +1,50 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module synchronizer #(
parameter WIDTH = 1,
parameter STAGES = 2,
parameter INITIAL_VAL = 0,
parameter FALSE_PATH_TO_IN = 1
)(
input clk,
input rst,
input [WIDTH-1:0] in,
output [WIDTH-1:0] out
);
//Q: Why do we have a separate impl and instantiate
//it with a different instance name based on this
//arbitrary parameter FALSE_PATH_TO_IN?
//A: To make constraining these synchronizers easier.
//We would like to write a single false path constraint
//for all synchronizers when the input is truly async.
//However other cases might require constraining the input
//of this module.
//To enable this, all clients that hook up async signals to
//the "in" port can set FALSE_PATH_TO_IN=1 (or use the default)
//and all clients that want the "in" delay to be constrained can
//set FALSE_PATH_TO_IN=0.
//In the XDC we can write the following async constraint:
//set_false_path -to [get_pins */synchronizer_false_path/stages[0].value_reg[0]/D]
//and this will take care of all instances of this module with FALSE_PATH_TO_IN==1
generate if (FALSE_PATH_TO_IN == 1) begin
synchronizer_impl #(
.WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
) synchronizer_false_path (
.clk(clk), .rst(rst), .in(in), .out(out)
);
end else begin
synchronizer_impl #(
.WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
) synchronizer_constrained (
.clk(clk), .rst(rst), .in(in), .out(out)
);
end endgenerate
endmodule //synchronizer
+47
View File
@@ -0,0 +1,47 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module synchronizer_impl #(
parameter WIDTH = 1,
parameter STAGES = 2,
parameter INITIAL_VAL = 0
)(
input clk,
input rst,
input [WIDTH-1:0] in,
output [WIDTH-1:0] out
);
(* ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] value[0:STAGES-1];
integer k;
initial begin
for (k = 0; k < STAGES; k = k + 1) begin
value[k] = INITIAL_VAL;
end
end
genvar i;
generate
for (i=0; i<STAGES; i=i+1) begin: stages
always @(posedge clk) begin
if (rst) begin
value[i] <= INITIAL_VAL;
end else begin
if (i == 0) begin
value[i] <= in;
end else begin
value[i] <= value[i-1];
end
end
end
end
endgenerate
assign out = value[STAGES-1];
endmodule //synchronizer_impl
+66
View File
@@ -0,0 +1,66 @@
//
// Copyright 2012 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// User settings bus
//
// Provides 8-bit address, 32-bit data write only bus for user settings, consumes to addresses in
// normal settings bus.
//
// Write user address to BASE
// Write user data to BASE+1
//
// The user_set_stb will strobe after data write, must write new address even if same as previous one.
module user_settings
#(parameter BASE=0)
(input clk,
input rst,
input set_stb,
input [7:0] set_addr,
input [31:0] set_data,
output set_stb_user,
output [7:0] set_addr_user,
output [31:0] set_data_user
);
wire addr_changed, data_changed;
reg stb_int;
setting_reg #(.my_addr(BASE+0),.width(8)) sr_0
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(set_addr_user),.changed(addr_changed) );
setting_reg #(.my_addr(BASE+1)) sr_1
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(set_data_user),.changed(data_changed) );
always @(posedge clk)
if (rst|set_stb_user)
stb_int <= 0;
else
if (addr_changed)
stb_int <= 1;
assign set_stb_user = stb_int & data_changed;
endmodule // user_settings
+14
View File
@@ -0,0 +1,14 @@
#
# Copyright 2013 Ettus Research LLC
# Copyright 2016 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
##################################################
# Control Lib Sources (2xx devices)
##################################################
CONTROL_LIB_200_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control_200/, \
cvita_uart.v \
radio_ctrl_proc.v \
))
+167
View File
@@ -0,0 +1,167 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//create a compressed vita based uart data interface
module cvita_uart
#(
parameter SIZE = 0
)
(
//clocking interface
input clk, input rst,
//uart interface
input rxd, output txd,
//chdr fifo input
input [63:0] i_tdata,
input i_tlast,
input i_tvalid,
output i_tready,
//chdr fifo output
output [63:0] o_tdata,
output o_tlast,
output o_tvalid,
input o_tready
);
reg [31:0] sid;
//baud clock divider
reg [15:0] clkdiv;
//hold rx in disable until a tx event
reg rxd_enable;
//==================================================================
//== RXD capture and packet generation interface
//==================================================================
wire [7:0] rx_char;
wire fifo_empty;
wire fifo_read;
reg [11:0] seqnum;
wire pgen_trigger;
wire pgen_done;
//rx uart capture
simple_uart_rx #(.SIZE(SIZE)) simple_uart_rx
(
.clk(clk), .rst(rst | ~rxd_enable),
.fifo_out(rx_char), .fifo_read(fifo_read), .fifo_level(), .fifo_empty(fifo_empty),
.clkdiv(clkdiv), .rx(rxd)
);
//packet generation - holds rx character
context_packet_gen context_packet_gen
(
.clk(clk), .reset(rst), .clear(1'b0),
.trigger(pgen_trigger),
.seqnum(seqnum),
.sid({sid[15:0], sid[31:16]}),
.body({56'b0, rx_char}),
.vita_time(64'b0),
.done(pgen_done),
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
);
//state machine to manage pgen and rx uart
reg [1:0] rxd_state;
localparam RXD_STATE_RECV_CHAR = 0;
localparam RXD_STATE_PGEN_TRIG = 1;
localparam RXD_STATE_WAIT_DONE = 2;
localparam RXD_STATE_READ_FIFO = 3;
always @(posedge clk) begin
if (rst) begin
seqnum <= 12'b0;
rxd_state <= RXD_STATE_RECV_CHAR;
end
else case (rxd_state)
RXD_STATE_RECV_CHAR: begin
if (!fifo_empty && rxd_enable) rxd_state <= RXD_STATE_PGEN_TRIG;
end
RXD_STATE_PGEN_TRIG: begin
rxd_state <= RXD_STATE_WAIT_DONE;
end
RXD_STATE_WAIT_DONE: begin
if (pgen_done) rxd_state <= RXD_STATE_READ_FIFO;
end
RXD_STATE_READ_FIFO: begin
rxd_state <= RXD_STATE_RECV_CHAR;
seqnum <= seqnum + 1'b1;
end
endcase //rxd_state
end
assign fifo_read = (rxd_state == RXD_STATE_READ_FIFO) || (!rxd_enable);
assign pgen_trigger = (rxd_state == RXD_STATE_PGEN_TRIG);
//==================================================================
//== TXD generation and packet control interface
//==================================================================
wire [7:0] tx_char;
wire fifo_write;
wire fifo_full;
simple_uart_tx #(.SIZE(SIZE)) simple_uart_tx
(
.clk(clk), .rst(rst),
.fifo_in(tx_char), .fifo_write(fifo_write), .fifo_level(), .fifo_full(fifo_full),
.clkdiv(clkdiv), .baudclk(), .tx(txd)
);
//state machine to manage control and tx uart
reg [1:0] txd_state;
localparam TXD_STATE_RECV_CHDR = 0;
localparam TXD_STATE_RECV_TIME = 1;
localparam TXD_STATE_RECV_BODY = 2;
localparam TXD_STATE_DROP_FIFO = 3;
always @(posedge clk) begin
if (rst) begin;
txd_state <= TXD_STATE_RECV_CHDR;
rxd_enable <= 1'b0;
end
if (i_tvalid && i_tready) case (txd_state)
TXD_STATE_RECV_CHDR: begin
txd_state <= (i_tdata[61])? TXD_STATE_RECV_TIME : TXD_STATE_RECV_BODY;
sid <= i_tdata[31:0];
end
TXD_STATE_RECV_TIME: begin
txd_state <= TXD_STATE_RECV_BODY;
end
TXD_STATE_RECV_BODY: begin
txd_state <= (i_tlast)? TXD_STATE_RECV_CHDR : TXD_STATE_DROP_FIFO;
clkdiv <= i_tdata[47:32];
rxd_enable <= 1'b1;
end
TXD_STATE_DROP_FIFO: begin
if (i_tlast) txd_state <= TXD_STATE_RECV_CHDR;
end
endcase //txd_state
end
assign tx_char = i_tdata[7:0];
assign fifo_write = (txd_state == TXD_STATE_RECV_BODY) && i_tvalid && i_tready;
assign i_tready = !fifo_full;
endmodule // cvita_uart
+151
View File
@@ -0,0 +1,151 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Radio Control Processor
// Accepts compressed vita extension context packets of the following form:
// { VITA Compressed Header, Stream ID }
// { Optional 64 bit time }
// { 16'h0, setting bus address [15:0], setting [31:0] }
//
// If there is a timestamp, packet is held until that time comes.
// Goes immediately if there is no timestamp or if time has passed.
// Sends out setting to setting bus, and then generates a response packet
// with the same sequence number, the src/dest swapped streamid, and the actual time
// the setting was sent.
//
// Note -- if t0 is the requested time, the actual send time on the setting bus is t0 + 1 cycle.
module radio_ctrl_proc
(input clk, input reset, input clear,
input [63:0] ctrl_tdata, input ctrl_tlast, input ctrl_tvalid, output reg ctrl_tready,
output reg [63:0] resp_tdata, output reg resp_tlast, output resp_tvalid, input resp_tready,
input [63:0] vita_time,
output set_stb, output [7:0] set_addr, output [31:0] set_data,
input ready,
input [63:0] readback,
output [31:0] debug);
localparam RC_HEAD = 4'd0;
localparam RC_TIME = 4'd1;
localparam RC_DATA = 4'd2;
localparam RC_DUMP = 4'd3;
localparam RC_RESP_HEAD = 4'd4;
localparam RC_RESP_TIME = 4'd5;
localparam RC_RESP_DATA = 4'd6;
wire IS_EC = ctrl_tdata[63];
wire HAS_TIME = ctrl_tdata[61];
reg HAS_TIME_reg;
reg [3:0] rc_state;
reg [63:0] cmd_time;
wire now, late, go;
reg [11:0] seqnum;
reg [31:0] sid;
always @(posedge clk)
if(reset)
begin
rc_state <= RC_HEAD;
HAS_TIME_reg <= 1'b0;
sid <= 32'd0;
seqnum <= 12'd0;
end
else
case(rc_state)
RC_HEAD :
if(ctrl_tvalid)
begin
sid <= ctrl_tdata[31:0];
seqnum <= ctrl_tdata[59:48];
HAS_TIME_reg <= HAS_TIME;
if(IS_EC)
if(HAS_TIME)
rc_state <= RC_TIME;
else
rc_state <= RC_DATA;
else
if(~ctrl_tlast)
rc_state <= RC_DUMP;
end
RC_TIME :
if(ctrl_tvalid)
if(ctrl_tlast)
rc_state <= RC_RESP_HEAD;
else if(go)
rc_state <= RC_DATA;
RC_DATA :
if(ctrl_tvalid)
if(ready)
if(ctrl_tlast)
rc_state <= RC_RESP_HEAD;
else
rc_state <= RC_DUMP;
RC_DUMP :
if(ctrl_tvalid)
if(ctrl_tlast)
rc_state <= RC_RESP_HEAD;
RC_RESP_HEAD :
if(resp_tready)
rc_state <= RC_RESP_TIME;
RC_RESP_TIME :
if(resp_tready)
rc_state <= RC_RESP_DATA;
RC_RESP_DATA:
if(resp_tready)
rc_state <= RC_HEAD;
default :
rc_state <= RC_HEAD;
endcase // case (rc_state)
always @*
case (rc_state)
RC_HEAD : ctrl_tready <= 1'b1;
RC_TIME : ctrl_tready <= ctrl_tlast | go;
RC_DATA : ctrl_tready <= ready;
RC_DUMP : ctrl_tready <= 1'b1;
default : ctrl_tready <= 1'b0;
endcase // case (rc_state)
time_compare time_compare
(.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(ctrl_tdata), .now(now), .early(), .late(late), .too_early());
assign go = now | late;
assign set_stb = (rc_state == RC_DATA) & ready & ctrl_tvalid;
assign set_addr = ctrl_tdata[39:32];
assign set_data = ctrl_tdata[31:0];
always @(posedge clk)
if (set_stb)
cmd_time <= vita_time;
always @*
case (rc_state)
RC_RESP_HEAD : { resp_tlast, resp_tdata } <= {1'b0, 4'hA, seqnum, 16'd24, sid[15:0], sid[31:16] };
RC_RESP_TIME : { resp_tlast, resp_tdata } <= {1'b0, cmd_time};
RC_RESP_DATA : { resp_tlast, resp_tdata } <= {1'b1, readback};
default : { resp_tlast, resp_tdata } <= 65'h0;
endcase // case (rc_state)
assign resp_tvalid = (rc_state == RC_RESP_HEAD) | (rc_state == RC_RESP_TIME) | (rc_state == RC_RESP_DATA);
endmodule // radio_ctrl_proc
+110
View File
@@ -0,0 +1,110 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`timescale 1ns/1ps
module radio_ctrl_proc_tb();
reg clk = 0;
reg reset = 1;
always #10 clk = ~clk;
initial $dumpfile("radio_ctrl_proc_tb.vcd");
initial $dumpvars(0,radio_ctrl_proc_tb);
initial
begin
#1000 reset = 0;
#20000;
$finish;
end
reg [63:0] vita_time = 64'd0;
always @(posedge clk)
if(reset) vita_time <= 64'd0;
else vita_time <= vita_time + 64'd1;
reg [63:0] tdata;
wire [63:0] tdata_int;
reg tlast;
wire tlast_int;
reg tvalid = 1'b0;
wire tvalid_int;
wire tready, tready_int;
wire [7:0] set_addr;
wire [31:0] set_data;
wire set_stb;
wire ready = 1'b1;
task send_packet;
input ec;
input timed;
input [11:0] seqnum;
input [31:0] sid;
input [63:0] vtime;
input [15:0] addr;
input [31:0] data;
begin
// Send a packet
@(posedge clk);
tlast <= 1'b0;
tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
tvalid <= 1;
@(posedge clk);
if(timed)
begin
tdata <= vtime;
@(posedge clk);
end
tlast <= 1'b1;
tdata <= { 16'h0, addr, data };
@(posedge clk);
tvalid <= 0;
@(posedge clk);
end
endtask // send_packet
initial
begin
tvalid <= 1'b0;
while(reset)
@(posedge clk);
send_packet(1'b1,1'b0,12'h5,32'hDEAD_BEEF,64'h0,16'hB,32'hF00D_1234);
send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
//send_packet(.ec(1), .timed(0), .seqnum(5), .sid(32'hDEAD_BEEF), .vtime(0), .addr(16'hB), .data(32'hF00D_1234));
end
axi_fifo_short #(.WIDTH(65)) axi_fifo_short
(.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
.o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int));
wire [63:0] resp_tdata;
wire resp_tlast, resp_tvalid, resp_tready;
radio_ctrl_proc radio_ctrl_proc
(.clk(clk), .reset(reset), .clear(1'b0),
.ctrl_tdata(tdata_int), .ctrl_tlast(tlast_int), .ctrl_tvalid(tvalid_int), .ctrl_tready(tready_int),
.resp_tdata(resp_tdata), .resp_tlast(resp_tlast), .resp_tvalid(resp_tvalid), .resp_tready(resp_tready),
.vita_time(vita_time), .ready(ready),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.debug()
);
assign resp_tready = 1'b1;
always @(posedge clk)
if(resp_tvalid & resp_tready)
begin
$display("%x",resp_tdata);
if(resp_tlast)
$display("TLAST");
end
endmodule // radio_ctrl_proc_tb
+47
View File
@@ -0,0 +1,47 @@
#
# Copyright 2013 Ettus Research LLC
# Copyright 2016 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
##################################################
# DSP Sources
##################################################
DSP_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/dsp/, \
acc.v \
add2_and_clip_reg.v \
add2_and_clip.v \
add2_and_round_reg.v \
add2_and_round.v \
add2_reg.v \
add2.v \
add_then_mac.v \
cic_decim.v \
cic_dec_shifter.v \
cic_interp.v \
cic_int_shifter.v \
cic_strober.v \
clip_reg.v \
clip.v \
cordic_stage.v \
cordic_z24.v \
ddc_chain.v \
duc_chain.v \
hb47_int.v \
hb_dec.v \
hb_interp.v \
Makefile.srcs \
mult_add_clip.v \
round_reg.v \
round_sd.v \
round.v \
rx_dcoffset.v \
rx_frontend.v \
sign_extend.v \
small_hb_dec.v \
small_hb_int.v \
srl.v \
tx_frontend.v \
variable_delay_line.v \
))
+36
View File
@@ -0,0 +1,36 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module acc
#(parameter IWIDTH=16, OWIDTH=30)
(input clk,
input clear,
input acc,
input [IWIDTH-1:0] in,
output reg [OWIDTH-1:0] out);
wire [OWIDTH-1:0] in_signext;
sign_extend #(.bits_in(IWIDTH),.bits_out(OWIDTH))
acc_signext (.in(in),.out(in_signext));
// CLEAR & ~ACC --> clears the accumulator
// CLEAR & ACC --> loads the accumulator
// ~CLEAR & ACC --> accumulates
// ~CLEAR & ~ACC --> hold
wire [OWIDTH-1:0] addend1 = clear ? 0 : out;
wire [OWIDTH-1:0] addend2 = ~acc ? 0 : in_signext;
wire [OWIDTH-1:0] sum_int = addend1 + addend2;
always @(posedge clk)
out <= sum_int;
endmodule // acc
+19
View File
@@ -0,0 +1,19 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2
#(parameter WIDTH=16)
(input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
output [WIDTH-1:0] sum);
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
assign sum = sum_int[WIDTH:1]; // Note -- will have some bias
endmodule // add2
+18
View File
@@ -0,0 +1,18 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2_and_clip
#(parameter WIDTH=16)
(input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
output [WIDTH-1:0] sum);
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip
(.in(sum_int),.out(sum));
endmodule // add2_and_clip
+30
View File
@@ -0,0 +1,30 @@
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2_and_clip_reg
#(parameter WIDTH=16)
(input clk,
input rst,
input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
input strobe_in,
output reg [WIDTH-1:0] sum,
output reg strobe_out);
wire [WIDTH-1:0] sum_int;
add2_and_clip #(.WIDTH(WIDTH)) add2_and_clip (.in1(in1),.in2(in2),.sum(sum_int));
always @(posedge clk)
if(rst)
sum <= 0;
else if(strobe_in)
sum <= sum_int;
always @(posedge clk) strobe_out <= rst ? 1'b0 : strobe_in;
endmodule // add2_and_clip_reg
+19
View File
@@ -0,0 +1,19 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2_and_round
#(parameter WIDTH=16)
(input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
output [WIDTH-1:0] sum);
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
assign sum = sum_int[WIDTH:1] + (sum_int[WIDTH] & sum_int[0]);
endmodule // add2_and_round
+24
View File
@@ -0,0 +1,24 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2_and_round_reg
#(parameter WIDTH=16)
(input clk,
input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
output reg [WIDTH-1:0] sum);
wire [WIDTH-1:0] sum_int;
add2_and_round #(.WIDTH(WIDTH)) add2_n_rnd (.in1(in1),.in2(in2),.sum(sum_int));
always @(posedge clk)
sum <= sum_int;
endmodule // add2_and_round_reg
+25
View File
@@ -0,0 +1,25 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module add2_reg
#(parameter WIDTH=16)
(input clk,
input [WIDTH-1:0] in1,
input [WIDTH-1:0] in2,
output reg [WIDTH-1:0] sum);
wire [WIDTH-1:0] sum_int;
add2 #(.WIDTH(WIDTH)) add2 (.in1(in1),.in2(in2),.sum(sum_int));
always @(posedge clk)
sum <= sum_int;
endmodule // add2_reg
+164
View File
@@ -0,0 +1,164 @@
//
// Copyright 2015 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
`timescale 1 ps / 1 ps
//
// Implements acc=((a+d)*b)+c or acc=((a+d)*b)+acc'
//
module add_then_mac
#(parameter DEVICE = "VIRTEX6")
(
// Output ports
output [47:0] acc,
// Input ports
input carryin,
input ce,
input clk,
input [17:0] b,
input load,
input [47:0] c,
input [17:0] a,
input [17:0] d,
input rst
);
wire [24:0] a_in;
wire [24:0] d_in;
localparam AREG_IN = 1;
localparam BREG_IN = 1;
localparam MREG_IN = 1;
localparam PREG_IN = 1;
localparam A1REG_IN = 1;
localparam A0REG_IN = 0;
localparam B1REG_IN = 1;
localparam B0REG_IN = 1;
// Sign extend inputs
assign a_in = (a[17] == 1'b1) ? {7'hff, a} : {7'h00, a};
assign d_in = (d[17] == 1'b1) ? {7'hff, d} : {7'h00, d};
generate
case(DEVICE)
// begin generate virtex6
"VIRTEX6", "7SERIES" :
begin
DSP48E1 #(
.ACASCREG(AREG_IN),
.AREG(AREG_IN),
.BCASCREG(BREG_IN),
.BREG(BREG_IN),
.MREG(MREG_IN),
.PREG(PREG_IN),
.USE_DPORT("TRUE")
)
DSP48E_BL (
.ACOUT(),
.BCOUT(),
.CARRYCASCOUT(),
.CARRYOUT(),
.MULTSIGNOUT(),
.OVERFLOW(),
.P(acc),
.PATTERNBDETECT(),
.PATTERNDETECT(),
.PCOUT(),
.UNDERFLOW(),
.A({5'b0, a_in[24:0]}),
.ACIN(30'b0),
.ALUMODE(4'b0000),
.B(b[17:0]),
.BCIN(18'b0),
.C(c),
.CARRYCASCIN(1'b0),
.CARRYIN(carryin),
.CARRYINSEL(3'b0),
.CEA1(1'b0),
.CEA2(ce),
.CEAD(ce),
.CEALUMODE(ce),
.CEB1(1'b0),
.CEB2(ce),
.CEC(ce),
.CECARRYIN(ce),
.CECTRL(ce),
.CED(ce),
.CEINMODE(ce),
.CEM(ce),
.CEP(ce),
.CLK(clk),
.D(d_in[24:0]),
.INMODE(5'b00100),
.MULTSIGNIN(1'b0),
.OPMODE({2'b01,load,4'b0101}),
.PCIN(48'b0),
.RSTA(rst),
.RSTALLCARRYIN(rst),
.RSTALUMODE(rst),
.RSTB(rst),
.RSTC(rst),
.RSTCTRL(rst),
.RSTD(rst),
.RSTINMODE(rst),
.RSTM(rst),
.RSTP(rst)
);
end // end generate virtex6
// begin generate spartan6
"SPARTAN6" :
begin
// DSP48A1 has 18b+18b=18b pre-adder, must discard LSB of A and D and compensate by shifting ACC.
wire discard;;
assign acc[0] = 1'b0;
DSP48A1 #(
.A0REG(A0REG_IN),
.A1REG(A1REG_IN),
.B0REG(B0REG_IN),
.B1REG(B1REG_IN),
.MREG(MREG_IN),
.PREG(PREG_IN)
)
DSP48AST (
.BCOUT(),
.CARRYOUT(),
.CARRYOUTF(),
.M(),
.P({discard,acc[47:1]}),
.PCOUT(),
.A(b[17:0]),
.B({a_in[17],a_in[17:1]}),
.C(c),
.CARRYIN(carryin),
.CEA(ce),
.CEB(ce),
.CEC(ce),
.CECARRYIN(ce),
.CED(ce),
.CEM(ce),
.CEOPMODE(ce),
.CEP(ce),
.CLK(clk),
.D({d_in[17],d_in[17:1]}),
.OPMODE({5'b00011,load, 2'b01}),
.PCIN(48'b0),
.RSTA(rst),
.RSTB(rst),
.RSTC(rst),
.RSTCARRYIN(rst),
.RSTD(rst),
.RSTM(rst),
.RSTOPMODE(rst),
.RSTP(rst)
);
end // end generate spartan6
endcase
endgenerate
endmodule
+96
View File
@@ -0,0 +1,96 @@
// -*- verilog -*-
//
// USRP - Universal Software Radio Peripheral
//
// Copyright (C) 2003 Matt Ettus
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// NOTE This only works for N=4, max decim rate of 128
// NOTE signal "rate" is EQUAL TO the actual rate, no more -1 BS
module cic_dec_shifter(rate,signal_in,signal_out);
parameter bw = 16;
parameter maxbitgain = 28;
input [7:0] rate;
input wire [bw+maxbitgain-1:0] signal_in;
output reg [bw-1:0] signal_out;
function [4:0] bitgain;
input [7:0] rate;
case(rate)
// Exact Cases -- N*log2(rate)
8'd1 : bitgain = 0;
8'd2 : bitgain = 4;
8'd4 : bitgain = 8;
8'd8 : bitgain = 12;
8'd16 : bitgain = 16;
8'd32 : bitgain = 20;
8'd64 : bitgain = 24;
8'd128 : bitgain = 28;
// Nearest without overflow -- ceil(N*log2(rate))
8'd3 : bitgain = 7;
8'd5 : bitgain = 10;
8'd6 : bitgain = 11;
8'd7 : bitgain = 12;
8'd9 : bitgain = 13;
8'd10,8'd11 : bitgain = 14;
8'd12,8'd13 : bitgain = 15;
8'd14,8'd15 : bitgain = 16;
8'd17,8'd18,8'd19 : bitgain = 17;
8'd20,8'd21,8'd22 : bitgain = 18;
8'd23,8'd24,8'd25,8'd26 : bitgain = 19;
8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 20;
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38 : bitgain = 21;
8'd39,8'd40,8'd41,8'd42,8'd43,8'd44,8'd45 : bitgain = 22;
8'd46,8'd47,8'd48,8'd49,8'd50,8'd51,8'd52,8'd53 : bitgain = 23;
8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 24;
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76 : bitgain = 25;
8'd77,8'd78,8'd79,8'd80,8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90 : bitgain = 26;
8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101,8'd102,8'd103,8'd104,8'd105,8'd106,8'd107 : bitgain = 27;
default : bitgain = 28;
endcase // case(rate)
endfunction // bitgain
wire [4:0] shift = bitgain(rate);
// We should be able to do this, but can't ....
// assign signal_out = signal_in[shift+bw-1:shift];
always @*
case(shift)
5'd0 : signal_out = signal_in[0+bw-1:0];
5'd4 : signal_out = signal_in[4+bw-1:4];
5'd7 : signal_out = signal_in[7+bw-1:7];
5'd8 : signal_out = signal_in[8+bw-1:8];
5'd10 : signal_out = signal_in[10+bw-1:10];
5'd11 : signal_out = signal_in[11+bw-1:11];
5'd12 : signal_out = signal_in[12+bw-1:12];
5'd13 : signal_out = signal_in[13+bw-1:13];
5'd14 : signal_out = signal_in[14+bw-1:14];
5'd15 : signal_out = signal_in[15+bw-1:15];
5'd16 : signal_out = signal_in[16+bw-1:16];
5'd17 : signal_out = signal_in[17+bw-1:17];
5'd18 : signal_out = signal_in[18+bw-1:18];
5'd19 : signal_out = signal_in[19+bw-1:19];
5'd20 : signal_out = signal_in[20+bw-1:20];
5'd21 : signal_out = signal_in[21+bw-1:21];
5'd22 : signal_out = signal_in[22+bw-1:22];
5'd23 : signal_out = signal_in[23+bw-1:23];
5'd24 : signal_out = signal_in[24+bw-1:24];
5'd25 : signal_out = signal_in[25+bw-1:25];
5'd26 : signal_out = signal_in[26+bw-1:26];
5'd27 : signal_out = signal_in[27+bw-1:27];
5'd28 : signal_out = signal_in[28+bw-1:28];
default : signal_out = signal_in[28+bw-1:28];
endcase // case(shift)
endmodule // cic_dec_shifter
+78
View File
@@ -0,0 +1,78 @@
// -*- verilog -*-
//
// USRP - Universal Software Radio Peripheral
//
// Copyright (C) 2003 Matt Ettus
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
module cic_decim
#(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7)
(input clock,
input reset,
input enable,
input [7:0] rate,
input strobe_in,
input strobe_out,
input [bw-1:0] signal_in,
output reg [bw-1:0] signal_out);
localparam maxbitgain = N * log2_of_max_rate;
wire [bw+maxbitgain-1:0] signal_in_ext;
reg [bw+maxbitgain-1:0] integrator [0:N-1];
reg [bw+maxbitgain-1:0] differentiator [0:N-1];
reg [bw+maxbitgain-1:0] pipeline [0:N-1];
reg [bw+maxbitgain-1:0] sampler;
integer i;
sign_extend #(bw,bw+maxbitgain)
ext_input (.in(signal_in),.out(signal_in_ext));
always @(posedge clock)
if(~enable)
for(i=0;i<N;i=i+1)
integrator[i] <= 0;
else if (strobe_in)
begin
integrator[0] <= integrator[0] + signal_in_ext;
for(i=1;i<N;i=i+1)
integrator[i] <= integrator[i] + integrator[i-1];
end
always @(posedge clock)
if(~enable)
begin
sampler <= 0;
for(i=0;i<N;i=i+1)
begin
pipeline[i] <= 0;
differentiator[i] <= 0;
end
end
else if (strobe_out)
begin
sampler <= integrator[N-1];
differentiator[0] <= sampler;
pipeline[0] <= sampler - differentiator[0];
for(i=1;i<N;i=i+1)
begin
differentiator[i] <= pipeline[i-1];
pipeline[i] <= pipeline[i-1] - differentiator[i];
end
end // if (enable && strobe_out)
wire [bw-1:0] signal_out_unreg;
cic_dec_shifter #(bw)
cic_dec_shifter(rate,pipeline[N-1],signal_out_unreg);
always @(posedge clock)
signal_out <= signal_out_unreg;
endmodule // cic_decim
+90
View File
@@ -0,0 +1,90 @@
// -*- verilog -*-
//
// USRP - Universal Software Radio Peripheral
//
// Copyright (C) 2003 Matt Ettus
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// NOTE This only works for N=4, max interp rate of 128
// NOTE signal "rate" is EQUAL TO the actual rate (no more -1 BS)
module cic_int_shifter(rate,signal_in,signal_out);
parameter bw = 16;
parameter maxbitgain = 21;
input [7:0] rate;
input wire [bw+maxbitgain-1:0] signal_in;
output reg [bw-1:0] signal_out;
function [4:0] bitgain;
input [7:0] rate;
case(rate)
// Exact Cases
8'd1 : bitgain = 0;
8'd2 : bitgain = 3;
8'd4 : bitgain = 6;
8'd8 : bitgain = 9;
8'd16 : bitgain = 12;
8'd32 : bitgain = 15;
8'd64 : bitgain = 18;
8'd128 : bitgain = 21;
// Nearest without overflow
8'd3 : bitgain = 5;
8'd5 : bitgain = 7;
8'd6 : bitgain = 8;
8'd7 : bitgain = 9;
8'd9,8'd10 : bitgain = 10;
8'd11,8'd12 : bitgain = 11;
8'd13,8'd14,8'd15 : bitgain = 12;
8'd17,8'd18,8'd19,8'd20 : bitgain = 13;
8'd21,8'd22,8'd23,8'd24,8'd25 : bitgain = 14;
8'd26,8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 15;
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38,8'd39,8'd40 : bitgain = 16;
8'd41,8'd42,8'd43,8'd44,8'd45,8'd46,8'd47,8'd48,8'd49,8'd50 : bitgain = 17;
8'd51,8'd52,8'd53,8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 18;
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76,8'd77,8'd78,8'd79,8'd80 : bitgain = 19;
8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90,8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101 : bitgain = 20;
default : bitgain = 21;
endcase // case(rate)
endfunction // bitgain
wire [4:0] shift = bitgain(rate);
// We should be able to do this, but can't ....
// assign signal_out = signal_in[shift+bw-1:shift];
always @*
case(shift)
5'd0 : signal_out = signal_in[0+bw-1:0];
5'd3 : signal_out = signal_in[3+bw-1:3];
5'd6 : signal_out = signal_in[6+bw-1:6];
5'd9 : signal_out = signal_in[9+bw-1:9];
5'd12 : signal_out = signal_in[12+bw-1:12];
5'd15 : signal_out = signal_in[15+bw-1:15];
5'd18 : signal_out = signal_in[18+bw-1:18];
5'd21 : signal_out = signal_in[21+bw-1:21];
5'd5 : signal_out = signal_in[5+bw-1:5];
5'd7 : signal_out = signal_in[7+bw-1:7];
5'd8 : signal_out = signal_in[8+bw-1:8];
5'd10 : signal_out = signal_in[10+bw-1:10];
5'd11 : signal_out = signal_in[11+bw-1:11];
5'd13 : signal_out = signal_in[13+bw-1:13];
5'd14 : signal_out = signal_in[14+bw-1:14];
5'd16 : signal_out = signal_in[16+bw-1:16];
5'd17 : signal_out = signal_in[17+bw-1:17];
5'd19 : signal_out = signal_in[19+bw-1:19];
5'd20 : signal_out = signal_in[20+bw-1:20];
default : signal_out = signal_in[21+bw-1:21];
endcase // case(shift)
endmodule // cic_int_shifter

Some files were not shown because too many files have changed in this diff Show More