├── .gitignore ├── LICENSE.txt ├── README.txt ├── misc └── Make.B100_qa.t ├── python └── fpga_sdrlib │ ├── __init__.py │ ├── b100.py │ ├── buildutils.py │ ├── channelizer │ ├── __init__.py │ ├── build.py │ ├── qa_channelizer.py │ └── qa_fpga_channelizer.py │ ├── config.py │ ├── conversions.py │ ├── fft │ ├── __init__.py │ ├── build.py │ ├── dit.py │ ├── qa_dit.py │ ├── qa_fft.py │ └── qa_fpga_dit.py │ ├── flow │ ├── __init__.py │ └── qa_flow.py │ ├── flter │ ├── __init__.py │ └── qa_flter.py │ ├── fpgamath │ ├── __init__.py │ └── qa_fpgamath.py │ ├── generate.py │ ├── message │ ├── __init__.py │ ├── build.py │ ├── msg_codes.py │ ├── msg_utils.py │ └── qa_message.py │ ├── testbench.py │ └── uhd │ ├── __init__.py │ ├── build.py │ └── qa_uhd.py └── verilog ├── channelizer ├── channelizer.v ├── dut_channelizer.v └── qa_channelizer.v.t ├── fft ├── buffer_BB_to_stage.v ├── butterfly.v ├── dit_series.v.t ├── dut_dit.v ├── mstore.v ├── qa_butterfly.v ├── qa_dit.v.t ├── qa_dit_series.v.t ├── qa_stage.v ├── qa_stage_to_stage.v.t ├── stage.v ├── stage_to_out.v ├── stage_to_stage.v.t └── twiddlefactors.v.t ├── flow ├── buffer_AA.v ├── buffer_BB.v ├── dut_split.v ├── qa_buffer_AA.v ├── qa_buffer_AA_burst.v ├── qa_buffer_BB.v ├── qa_split.v └── split.v ├── flter ├── filter.v.t ├── filterbank.v ├── qa_filter.v ├── qa_filterbank.v └── summult.v.t ├── fpgamath ├── log.v ├── multiply.v ├── multiply_complex.v ├── qa_multiply.v └── qa_multiply_complex.v ├── message ├── dut_message_slicer.v ├── dut_message_stream_combiner.v ├── message_slicer.v ├── message_stream_combiner.v ├── qa_combo.v ├── qa_debug.v ├── qa_message_stream_combiner_bits.v ├── qa_message_stream_combiner_one.v ├── qa_sample_msg_splitter.v ├── qa_sample_msg_splitter_returns_msgs.v ├── qa_splitcombiner.v ├── sample_msg_splitter.v └── smaller.v └── uhd ├── bits.v ├── dut_qa_contents.v ├── dut_qa_wrapper.v ├── qa_wrapper.v ├── qa_wrapper_bits.v ├── qa_wrapper_null.v ├── u1plus_core_QA.v └── u1plus_core_loop.v /.gitignore: -------------------------------------------------------------------------------- 1 | \#* 2 | *~ 3 | .\#* 4 | *.pyc -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Ben Reynwar 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | fpga_sdrlib is a collection of verilog modules for software-defined radios. 2 | 3 | This framework is not yet documented well enough that it'll be easy to use for 4 | anyone but me. However if you do want to have a play with it then: 5 | - Requirements: python, UHD source, GNU Radio, MyHDL, Icarus Verilog and quite 6 | possibly some other python packages. 7 | - update fpga_sdrlib/python/fpga_sdrlib/config.py 8 | uhddir needs to be set to the location of the uhd source 9 | fpgaimag_fn should be the location the USRP looks for the FPGA image and the file 10 | should be writable. 11 | 12 | The subsections are: 13 | uhd - Wrappers to insert modules into the B100 FPGA. 14 | fpgamath - Utility math modules. 15 | flow - Modules that change the flow of data (currently a buffer and splitter). 16 | message - Modules for combining and separating message and data streams. 17 | flter - Filter modules. 18 | fft - FFT modules (currently not working). 19 | channelizer - A module for polyphase-filterbank channelization (currently not working). 20 | 21 | Building and Testing: 22 | The framework contains python code to automate the process of 23 | building and testing. For example fpga_sdrlib/verilog/flter 24 | contains the verilog code for filter modules and 25 | fpga_sdrlib/python/fpga_sdrlib/flter contains the python code for 26 | building and testing the flter modules. In particular qa_flter.py 27 | uses the python unit-testing framework to test the modules with 28 | MyHDL and Icarus, synthesise each module into the B100 FPGA code, 29 | update the FPGA image, and then test the image on the B100. The 30 | B100 image is modified for testing so that data flows into the B100 31 | through the transmit chain, through the module under test and then 32 | back along the receive chain. 33 | 34 | The interface for a typical module is: 35 | Inputs: 36 | clk: clock signal 37 | rst_n: reset when low 38 | in_data: a complex input 39 | in_m: meta_data synchronous with in_data 40 | in_nd: high if new data is in in_data 41 | in_msg_data: message stream data 42 | in_msg_nd: high if new message stream data is in in_msg_data 43 | Outputs: 44 | out_data: a complex output 45 | out_m: meta_data synchronous with out_data 46 | out_nd: high if new data is in in_data 47 | out_msg_data: message stream data 48 | out_msg_nd: high if new message stream data is in out_msg_data 49 | error: high if an error has occured 50 | 51 | The idea behind the message streams is that they can be used for 52 | things like setting taps or returning debug messages. 53 | 54 | Currently when using a B100 the message streams are sent and received 55 | with the complex data stream by reducing the complex numbers from 32 56 | bits to 30 bits and using one of the extra bits to indicate a message 57 | packet header. 58 | 59 | It's not a pretty solution but it seems to work. 60 | -------------------------------------------------------------------------------- /misc/Make.B100_qa.t: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2008-2012 Ettus Research LLC 3 | # 4 | 5 | ################################################## 6 | # Project Setup 7 | ################################################## 8 | TOP_MODULE := B100 9 | BUILD_DIR := {{build_dir}} 10 | CUSTOM_SRC_DIR := {{custom_src_dir}} 11 | 12 | # set me in a custom makefile 13 | CUSTOM_SRCS = 14 | CUSTOM_DEFS = 15 | 16 | ################################################## 17 | # Include other makefiles 18 | ################################################## 19 | 20 | include ../Makefile.common 21 | include ../../fifo/Makefile.srcs 22 | include ../../control_lib/Makefile.srcs 23 | include ../../sdr_lib/Makefile.srcs 24 | include ../../serdes/Makefile.srcs 25 | include ../../simple_gemac/Makefile.srcs 26 | include ../../timing/Makefile.srcs 27 | include ../../opencores/Makefile.srcs 28 | include ../../vrt/Makefile.srcs 29 | include ../../udp/Makefile.srcs 30 | include ../../coregen/Makefile.srcs 31 | include ../../gpif/Makefile.srcs 32 | 33 | ################################################## 34 | # Project Properties 35 | ################################################## 36 | export PROJECT_PROPERTIES := \ 37 | family "Spartan3A" \ 38 | device XC3S1400A \ 39 | package ft256 \ 40 | speed -4 \ 41 | top_level_module_type "HDL" \ 42 | synthesis_tool "XST (VHDL/Verilog)" \ 43 | simulator "ISE Simulator (VHDL/Verilog)" \ 44 | "Preferred Language" "Verilog" \ 45 | "Enable Message Filtering" FALSE \ 46 | "Display Incremental Messages" FALSE 47 | 48 | ################################################## 49 | # Sources 50 | ################################################## 51 | TOP_SRCS = \ 52 | B100.v \ 53 | {% for f in inputfiles %}{{f}} \ 54 | {% endfor %}B100.ucf \ 55 | timing.ucf 56 | 57 | SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ 58 | $(CONTROL_LIB_SRCS) $(SDR_LIB_SRCS) $(SERDES_SRCS) \ 59 | $(SIMPLE_GEMAC_SRCS) $(TIMING_SRCS) $(OPENCORES_SRCS) \ 60 | $(VRT_SRCS) $(UDP_SRCS) $(COREGEN_SRCS) $(EXTRAM_SRCS) \ 61 | $(GPIF_SRCS) 62 | 63 | ################################################## 64 | # Process Properties 65 | ################################################## 66 | SYNTHESIZE_PROPERTIES = \ 67 | "Number of Clock Buffers" 8 \ 68 | "Pack I/O Registers into IOBs" Yes \ 69 | "Optimization Effort" High \ 70 | "Optimize Instantiated Primitives" TRUE \ 71 | "Register Balancing" Yes \ 72 | "Use Clock Enable" Auto \ 73 | "Use Synchronous Reset" Auto \ 74 | "Use Synchronous Set" Auto \ 75 | "Verilog Macros" "$(CUSTOM_DEFS)" 76 | 77 | TRANSLATE_PROPERTIES = \ 78 | "Macro Search Path" "$(shell pwd)/../../coregen/" 79 | 80 | MAP_PROPERTIES = \ 81 | "Generate Detailed MAP Report" TRUE \ 82 | "Allow Logic Optimization Across Hierarchy" TRUE \ 83 | "Map to Input Functions" 4 \ 84 | "Optimization Strategy (Cover Mode)" Speed \ 85 | "Pack I/O Registers/Latches into IOBs" "For Inputs and Outputs" \ 86 | "Perform Timing-Driven Packing and Placement" TRUE \ 87 | "Map Effort Level" High \ 88 | "Extra Effort" Normal \ 89 | "Combinatorial Logic Optimization" TRUE \ 90 | "Register Duplication" TRUE 91 | 92 | PLACE_ROUTE_PROPERTIES = \ 93 | "Place & Route Effort Level (Overall)" High 94 | 95 | STATIC_TIMING_PROPERTIES = \ 96 | "Number of Paths in Error/Verbose Report" 10 \ 97 | "Report Type" "Error Report" 98 | 99 | GEN_PROG_FILE_PROPERTIES = \ 100 | "Configuration Rate" 6 \ 101 | "Create Binary Configuration File" TRUE \ 102 | "Done (Output Events)" 5 \ 103 | "Enable Bitstream Compression" TRUE \ 104 | "Enable Outputs (Output Events)" 6 \ 105 | "Unused IOB Pins" "Pull Up" 106 | 107 | SIM_MODEL_PROPERTIES = "" 108 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | sdrlib is a collection of verilog modules for creating software defined radios. 3 | Although since it's verilog maybe that doesn't count as software-defined. 4 | Anyway, this python package controls the generation of verilog from templates 5 | and the verification of the modules. 6 | """ 7 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/b100.py: -------------------------------------------------------------------------------- 1 | """ 2 | Synthesise a QA module into the B100 FPGA. 3 | """ 4 | 5 | import os 6 | import shutil 7 | import subprocess 8 | 9 | 10 | from jinja2 import Environment, FileSystemLoader 11 | 12 | from fpga_sdrlib import config 13 | from fpga_sdrlib.config import uhddir, miscdir, fpgaimage_fn 14 | 15 | b100dir = os.path.join(uhddir, 'fpga', 'usrp2', 'top', 'B100') 16 | custom_src_dir = os.path.join(config.verilogdir, 'uhd') 17 | 18 | def set_image(fn): 19 | shutil.copyfile(fn, fpgaimage_fn) 20 | 21 | def make_defines_file(builddir, defines): 22 | fn = os.path.join(builddir, 'global_defines.vh') 23 | f = open(fn, 'w') 24 | f.write(make_defines_prefix(defines)) 25 | f.close 26 | return fn 27 | 28 | def make_defines_prefix(defines): 29 | lines = [] 30 | for k, v in defines.items(): 31 | if v is False: 32 | pass 33 | elif v is True: 34 | lines.append('`define {0}'.format(k)) 35 | else: 36 | lines.append('`define {0} {1}'.format(k, v)) 37 | txt = '\n'.join(lines) 38 | txt += '\n' 39 | return txt 40 | 41 | def prefix_defines(fn, defines): 42 | f = open(fn) 43 | contents = f.read() 44 | f.close() 45 | prefix = make_defines_prefix(defines) 46 | f = open(fn, 'w') 47 | f.write(prefix) 48 | f.write(contents) 49 | f.close() 50 | 51 | def make_make(name, builddir, inputfiles, defines): 52 | header = make_defines_file(builddir, defines) 53 | #shutil.copy(header, os.path.join(config.builddir, 'message')) 54 | inputfiles = [header] + inputfiles 55 | output_fn = os.path.join(builddir, 'Make.B100_{name}'.format(name=name)) 56 | template_fn = 'Make.B100_qa.t' 57 | env = Environment(loader=FileSystemLoader(miscdir)) 58 | template = env.get_template(template_fn) 59 | f_out = open(output_fn, 'w') 60 | output_dir = os.path.join(builddir, 'build-B100_{name}'.format(name=name)) 61 | custom_defs = [] 62 | for k, v in defines.items(): 63 | if k == 'DEBUG': 64 | custom_defs.append(k) 65 | else: 66 | custom_defs.append("{0}={1}".format(k, v)) 67 | custom_defs = " | ".join(custom_defs) 68 | f_out.write(template.render(build_dir=output_dir, 69 | custom_src_dir=custom_src_dir, 70 | inputfiles=inputfiles, 71 | #custom_defs=custom_defs, 72 | )) 73 | f_out.close() 74 | 75 | def synthesise(name, builddir): 76 | output_dir = os.path.join(builddir, 'build-B100_{name}'.format(name=name)) 77 | # Synthesise 78 | currentdir = os.getcwd() 79 | os.chdir(b100dir) 80 | if os.path.exists(output_dir): 81 | shutil.rmtree(output_dir) 82 | make_fn = os.path.join(builddir, 'Make.B100_{0}'.format(name)) 83 | logfile_fn = os.path.join(builddir, 'Make.B100_{0}.log'.format(name)) 84 | logfile = open(logfile_fn, 'w') 85 | p = subprocess.Popen(['make', '-f', make_fn], 86 | stdout=logfile, stderr=logfile) 87 | p.wait() 88 | logfile.flush() 89 | logfile.close() 90 | # Check if last line is 91 | # All constraints were met 92 | f = open(logfile_fn, 'r') 93 | lines = f.readlines() 94 | lastline = lines[-2] 95 | if lastline != 'All constraints were met.\n': 96 | raise StandardError("Synthesis failed: see {0}".format(logfile_fn)) 97 | f.close() 98 | os.chdir(currentdir) 99 | return os.path.join(output_dir, 'B100.bin') 100 | 101 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/buildutils.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os 3 | import logging 4 | import filecmp 5 | 6 | from fpga_sdrlib import config 7 | from fpga_sdrlib import message, uhd, flow, flter, fpgamath, fft 8 | from fpga_sdrlib import b100 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | def make_define_string(defines): 13 | definestrs = [] 14 | for k, v in defines.items(): 15 | if v is True or v is False: 16 | if v is True: 17 | definestrs.append("-D" + k) 18 | else: 19 | definestrs.append("-D{0}={1}".format(k, v)) 20 | return ' '.join(definestrs) 21 | 22 | def generate_block(package, filename, extraargs={}, included_dependencies=set(), 23 | include_filenames=[]): 24 | included_dependencies.add((package, filename)) 25 | dependencies, generating_function, args = blocks[package][filename] 26 | if dependencies is None: 27 | dependencies = [] 28 | include_filename, dep_extraargs = generating_function( 29 | package, filename, dependencies, extraargs, **args) 30 | include_filenames.append(include_filename) 31 | if dependencies is None: 32 | dependencies = [] 33 | if args is None: 34 | args = {} 35 | for d in dependencies: 36 | bits = d.split('/') 37 | if len(bits) == 1: 38 | pck = package 39 | fn = bits[0] 40 | else: 41 | pck = bits[0] 42 | fn = bits[1] 43 | if d in dep_extraargs: 44 | extraargs = dep_extraargs[d] 45 | else: 46 | extraargs = {} 47 | if (pck, fn) not in included_dependencies: 48 | generate_block(pck, fn, extraargs, included_dependencies, include_filenames) 49 | return included_dependencies, include_filenames 50 | 51 | def d2pd(package, dependency): 52 | bits = dependency.split('/') 53 | if len(bits) == 1: 54 | pck = package 55 | fn = bits[0] 56 | else: 57 | pck = bits[0] 58 | fn = bits[1] 59 | return (pck, fn) 60 | 61 | def pd2fn(pck, fn): 62 | return os.path.join(config.verilogdir, pck, fn) 63 | 64 | def generate_B100_image(package, name, suffix, defines=config.default_defines, extraargs={}): 65 | builddir = os.path.join(config.builddir, package) 66 | outputdir = os.path.join(builddir, 'build-B100_{name}{suffix}'.format( 67 | name=name, suffix=suffix)) 68 | vdir = os.path.join(builddir, 'verilog-B100_{name}{suffix}'.format( 69 | name=name, suffix=suffix)) 70 | if not os.path.exists(vdir): 71 | os.makedirs(vdir) 72 | dependencies = compatibles[package][name] 73 | included_dependencies = set() 74 | inputfiles = [os.path.join(pd2fn('uhd', 'u1plus_core_QA.v'))] 75 | for d in dependencies: 76 | pck, fn = d2pd(package, d) 77 | if (pck, fn) not in included_dependencies: 78 | generate_block(pck, fn, extraargs, included_dependencies, inputfiles) 79 | new_inputfiles= [] 80 | changed = False 81 | for f in inputfiles: 82 | # Prefix the macros to the beginning of each file. 83 | # FIXME: There has to be a better way to get this working :(. 84 | assert(f.endswith('.v')) 85 | bn = os.path.basename(f) 86 | f2 = os.path.join(vdir, bn[:-2] + '_prefixed.v') 87 | f3 = os.path.join(vdir, bn[:-2] + '_final.v') 88 | shutil.copyfile(f, f2) 89 | b100.prefix_defines(f2, defines) 90 | # See if any of the produced files are different than what 91 | # was used last time. 92 | if (not os.path.exists(f3)) or (not filecmp.cmp(f2, f3)): 93 | changed = True 94 | shutil.copyfile(f2, f3) 95 | new_inputfiles.append(f3) 96 | image_fn = os.path.join(outputdir, 'B100.bin') 97 | if changed or not os.path.exists(image_fn): 98 | b100.make_make(name+suffix, builddir, new_inputfiles, defines) 99 | return b100.synthesise(name+suffix, builddir) 100 | else: 101 | return image_fn 102 | 103 | def generate_icarus_executable(package, name, suffix, defines=config.default_defines, extraargs={}): 104 | builddir = os.path.join(config.builddir, package) 105 | if name in compatibles[package]: 106 | dependencies = compatibles[package][name] 107 | dependencies = list(dependencies) 108 | dependencies.append('uhd/dut_qa_wrapper.v') 109 | else: 110 | dependencies = incompatibles[package][name] 111 | included_dependencies = set() 112 | inputfiles = [] 113 | for d in dependencies: 114 | pck, fn = d2pd(package, d) 115 | if (pck, fn) not in included_dependencies: 116 | generate_block(pck, fn, extraargs, included_dependencies, inputfiles) 117 | inputfilestr = ' '.join(inputfiles) 118 | print(inputfilestr) 119 | executable = name + suffix 120 | executable = os.path.join(builddir, executable) 121 | definestr = make_define_string(defines) 122 | cmd = ("iverilog -o {executable} {definestr} {inputfiles}" 123 | ).format(executable=executable, 124 | definestr=definestr, 125 | inputfiles=inputfilestr) 126 | logger.debug(cmd) 127 | os.system(cmd) 128 | return executable 129 | 130 | packages = {'message': message, 131 | 'uhd': uhd, 132 | 'flow': flow, 133 | 'flter': flter, 134 | 'fpgamath': fpgamath, 135 | 'fft': fft, 136 | } 137 | 138 | blocks = dict([(key, getattr(sp, 'blocks')) for 139 | key, sp in packages.items()]) 140 | 141 | compatibles = dict([(key, getattr(sp, 'compatibles')) for 142 | key, sp in packages.items()]) 143 | 144 | incompatibles = dict([(key, getattr(sp, 'incompatibles')) for 145 | key, sp in packages.items()]) 146 | 147 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/channelizer/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog sdrlib/channelizer code. 4 | """ 5 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/channelizer/build.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import os 5 | import math 6 | import shutil 7 | import logging 8 | 9 | from fpga_sdrlib.fft.build import generate_dit_files 10 | from fpga_sdrlib.filterbank.build import generate_filterbank_files 11 | from fpga_sdrlib import config 12 | from fpga_sdrlib.buildutils import copyfile, format_template, make_define_string 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | def get_builddir(): 17 | fftbuilddir = os.path.join(config.builddir, 'channelizer') 18 | if not os.path.exists(fftbuilddir): 19 | os.makedirs(fftbuilddir) 20 | return fftbuilddir 21 | 22 | def generate_channelizer_files(n_chans, width, filter_length): 23 | """ 24 | Generate the channelizer module files. 25 | 26 | Args: 27 | n_chans: Number of channels to split into. 28 | width: The width of a complex number 29 | (actually required so we can get the twiddle factor widths for fft). 30 | filter_length: The length of each filter in the filterbank. 31 | """ 32 | builddir = get_builddir() 33 | logn = math.log(n_chans)/math.log(2) 34 | if int(logn) != logn: 35 | raise ValueError("Number of channels must be a power of two.") 36 | # Divide width by 2 since generate_dit_files takes real width not complex width. 37 | inputfiles = generate_dit_files(n_chans, width/2) 38 | inputfiles += generate_filterbank_files(filter_length) 39 | inputfiles.append(copyfile('channelizer', 'channelizer.v')) 40 | # Remove repeated dependencies 41 | inputfiles = list(set(inputfiles)) 42 | return inputfiles 43 | 44 | def generate_channelizer_executable(name, n_chans, width, filter_length, defines): 45 | """ 46 | Generate an icarus verilog channelizer executable. 47 | 48 | Args: 49 | name: A name to identify the executable by. 50 | n_chans: Number of channels to split into. 51 | width: The width of a complex number 52 | (actually required so we can get the twiddle factor widths for fft). 53 | filter_length: The length of each filter in the filterbank. 54 | defines: Macro definitions for the verilog files. 55 | """ 56 | builddir = get_builddir() 57 | inputfiles = generate_channelizer_files(n_chans, width, filter_length) 58 | dut_channelizer_fn = copyfile('channelizer', 'dut_channelizer.v') 59 | executable = "channelizer_{name}".format(name=name) 60 | executable = os.path.join(config.builddir, 'channelizer', executable) 61 | inputfilestr = ' '.join(inputfiles + [dut_channelizer_fn]) 62 | defines.update({ 63 | 'N': n_chans, 64 | 'LOG_N': int(math.ceil(math.log(n_chans)/math.log(2))), 65 | 'FLTLEN': filter_length, 66 | 'LOG_FLTLEN': int(math.ceil(math.log(filter_length)/math.log(2))), 67 | }) 68 | definestr = make_define_string(defines) 69 | cmd = ("iverilog -o {executable} {definestr} {inputfiles}" 70 | ).format(executable=executable, 71 | definestr=definestr, 72 | inputfiles=inputfilestr) 73 | logger.debug(cmd) 74 | os.system(cmd) 75 | return executable 76 | 77 | def make_taps(taps, n_chans): 78 | extra_taps = int(math.ceil(1.0*len(taps)/n_chans)*n_chans - len(taps)) 79 | taps = taps + [0] * extra_taps 80 | # Make taps for each channel 81 | chantaps = [list(reversed(taps[i: len(taps): n_chans])) for i in range(0, n_chans)] 82 | for taps in chantaps: 83 | summedtaps = sum(taps) 84 | if summedtaps < -1 or summedtaps > 1: 85 | raise ValueError("Summed taps for each channel must be between -1 and 1 (Value is {0}).".format(summedtaps)) 86 | return chantaps 87 | 88 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/channelizer/qa_fpga_channelizer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | """ 5 | QA to check the vericode channelizer on the FPGA. 6 | """ 7 | 8 | import os 9 | import random 10 | import unittest 11 | import logging 12 | import math 13 | 14 | from gnuradio import uhd, gr 15 | 16 | from fpga_sdrlib.data_source.build import generate as data_source_generate 17 | from fpga_sdrlib.channelizer.build import generate as channelizer_generate 18 | from fpga_sdrlib import config 19 | from fpga_sdrlib import b100 20 | from fpga_sdrlib.channelizer import qa_channelizer as qach 21 | 22 | class TestChannelizer(unittest.TestCase): 23 | """ 24 | Test the verilog channelizer on B100. 25 | """ 26 | 27 | def setUp(self): 28 | # Number of channels 29 | self.M = 4 30 | self.logM = int(math.log(self.M)/math.log(2)) 31 | # The amount of data to send 32 | self.n_data = self.M * 8 33 | # Baseband sampling rate 34 | self.fs = 1000 35 | # Input samp rate to channelizer 36 | self.ifs = self.M*self.fs 37 | # Each channel contains a pure frequency with an offset and 38 | # amplitude. 39 | self.freqs = [0, 100, 200, -300] 40 | self.amplitudes = [1, 1, -0.2, 0.5] 41 | # Random number generator 42 | rg = random.Random(0) 43 | self.myrand = rg.random 44 | self.myrandint = rg.randint 45 | # Width of a complex number 46 | self.width = 32 47 | # Generate some taps 48 | self.taps, self.tapscale = qach.get_channelizer_taps(self.M, n_taps=8) 49 | # How often to send input. 50 | # For large FFTs this must be larger since the speed scales as MlogM. 51 | # Otherwise we get an overflow error. 52 | self.sendnth = 2 53 | # Get the input data 54 | self.data = qach.get_mixed_sinusoids(self.fs, self.n_data, self.freqs, self.amplitudes) 55 | # Scale the input data to remain in (-1 to 1) 56 | datamax = 0 57 | for d in self.data: 58 | datamax = max(datamax, abs(d.real), abs(d.imag)) 59 | self.inputscale = datamax 60 | self.data = [d/datamax for d in self.data] 61 | # Send in some meta data 62 | self.mwidth = 1 63 | self.ms = [self.myrandint(0, 2) for d in self.data] 64 | name = 'qachannelizer1' 65 | 66 | executable, ds_inputfiles = data_source_generate( 67 | name, self.data, self.ms, self.sendnth, self.width, self.mwidth) 68 | executable, ch_inputfiles = channelizer_generate( 69 | name, self.M, self.taps, self.width, self.mwidth, 70 | {'sendnth': self.sendnth, 'n_data': self.n_data}) 71 | qa_channelizer_fn = os.path.join(config.builddir, 'channelizer', 'qa_channelizer.v') 72 | b100.make_make('channelizer', ds_inputfiles + ch_inputfiles + [qa_channelizer_fn]) 73 | b100.synthesise('channelizer') 74 | b100.copy_image('channelizer') 75 | 76 | 77 | def tearDown(self): 78 | pass 79 | 80 | def test_channelizer(self): 81 | """ 82 | Test a channelizer. 83 | """ 84 | steps_rqd = self.n_data * self.sendnth + 1000 85 | self.tb.simulate(steps_rqd) 86 | received = [x*self.M for x in self.tb.output] 87 | skip = int(math.ceil(float(len(self.taps))/self.M-1)*self.M) 88 | received = [received[i+skip::self.M] for i in range(self.M)] 89 | expected = qach.get_expected_channelized_data( 90 | self.n_data/self.M, self.freqs, self.amplitudes) 91 | p_convolved, p_final = pychannelizer(self.taps, self.data, self.M) 92 | for ed, dd, pd in zip(expected, received, p_final): 93 | pd = [p*self.tapscale*self.inputscale for p in pd] 94 | dd = [d*self.tapscale*self.inputscale for d in dd] 95 | epf = ed[-1]/pd[-1] 96 | rpd = [p*epf for p in pd] 97 | self.assertTrue(len(rpd) != 0) 98 | self.assertTrue(len(ed) != 0) 99 | self.assertTrue(len(pd) != 0) 100 | for e, p in zip(ed, rpd): 101 | self.assertAlmostEqual(e, p, 3) 102 | for d, p in zip(dd, pd): 103 | self.assertAlmostEqual(d, p, 3) 104 | # Compare ms 105 | self.assertEqual(len(self.tb.out_ms), len(self.ms)) 106 | for r, e in zip(self.tb.out_ms, self.ms): 107 | self.assertEqual(r, e) 108 | # Compare first_channel signals 109 | fcs = ([1] + [0]*(self.M-1)) * (self.n_data/self.M) 110 | self.assertEqual(len(self.tb.out_fc), len(fcs)) 111 | for r, e in zip(self.tb.out_fc, fcs): 112 | self.assertEqual(r, e) 113 | 114 | 115 | 116 | if __name__ == '__main__': 117 | config.setup_logging(logging.DEBUG) 118 | unittest.main() 119 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import logging 5 | import os 6 | import math 7 | from copy import copy 8 | from os.path import dirname 9 | 10 | def logceil(n): 11 | val = int(math.ceil(float(math.log(n))/math.log(2))) 12 | # To keep things simple never return 0. 13 | # Declaring reg with 0 length is not legal. 14 | if val == 0: 15 | val = 1 16 | return val 17 | 18 | uhddir = os.path.join('/', 'home', 'ben', 'Code', 'uhd') 19 | fpgaimage_fn = "/usr/local/share/uhd/images/usrp_b100_fpga.bin" 20 | basedir = dirname(dirname(dirname(__file__))) 21 | miscdir = os.path.join(basedir, 'misc') 22 | verilogdir = os.path.join(basedir, 'verilog') 23 | builddir = os.path.join(basedir, 'build') 24 | 25 | default_sendnth = 4 26 | default_width = 32 27 | default_mwidth = 1 28 | default_debug = False 29 | default_log_sendnth = 6 30 | errorcode = 666 31 | msg_width = 32 32 | msg_length_width = 10 33 | msg_formatcode_width = 4 34 | msg_modulecode_width = 10 35 | msg_errorcode_width = 7 36 | # The buffer size for the msg/sample combiner in qa_wrapper. 37 | default_combiner_buffer_length = 128 38 | # How many bits to chop off real numbers in the complex stream 39 | # so that we get some header bits. 40 | msg_shift = 1 41 | 42 | default_defines = { 43 | "DEBUG": default_debug, 44 | "WIDTH": default_width, 45 | "MWIDTH": default_mwidth, 46 | "ERRORCODE": errorcode, 47 | 'MSG_WIDTH': msg_width, 48 | 'MSG_LENGTH_WIDTH': msg_length_width, 49 | 'MSG_FORMATCODE_WIDTH': msg_formatcode_width, 50 | 'MSG_MODULECODE_WIDTH': msg_modulecode_width, 51 | 'MSG_ERRORCODE_WIDTH': msg_errorcode_width, 52 | 'MSG_SHIFT': msg_shift, 53 | 'LOG_SENDNTH': default_log_sendnth, 54 | 'COMBINER_BUFFER_LENGTH': default_combiner_buffer_length, 55 | 'LOG_COMBINER_BUFFER_LENGTH': logceil(default_combiner_buffer_length), 56 | 'MAX_PACKET_LENGTH': pow(2, msg_formatcode_width)-1, 57 | } 58 | 59 | def updated_defines(updates): 60 | defines = copy(default_defines) 61 | defines.update(updates) 62 | return defines 63 | 64 | def setup_logging(level): 65 | "Utility function for setting up logging." 66 | ch = logging.StreamHandler() 67 | ch.setLevel(logging.DEBUG) 68 | formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 69 | ch.setFormatter(formatter) 70 | # Which packages do we want to log from. 71 | packages = ('__main__', 'fpga_sdrlib',) 72 | for package in packages: 73 | logger = logging.getLogger(package) 74 | logger.addHandler(ch) 75 | logger.setLevel(level) 76 | # Warning only packages 77 | packages = [] 78 | for package in packages: 79 | logger = logging.getLogger(package) 80 | logger.addHandler(ch) 81 | logger.setLevel(logging.WARNING) 82 | 83 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/conversions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | """ 5 | Functions to convert between complex numbers and integers. 6 | 7 | All complex numbers components must be between -1 and 1. 8 | 9 | The real component goes to the high bits of the integer and the imaginary 10 | component to the low bits. 11 | 12 | """ 13 | 14 | def c_to_int(c, x_width): 15 | """ 16 | Takes a complex number and a width. 17 | Converts to an integer of length x_width*2 bits. 18 | """ 19 | # Real part in high x_width bits. 20 | # Imag part in low x_width bits. 21 | # Complex components must be between -1 and 1. 22 | i = f_to_int(c.real, x_width) 23 | q = f_to_int(c.imag, x_width) 24 | return i * pow(2, x_width) + q 25 | 26 | def cs_to_int(cs, x_width): 27 | """ 28 | Takes a list of complex numbers and a width. 29 | Converts to an integer of length (x_width*2*len(C)) bits. 30 | """ 31 | multipler = 1 32 | combined = 0 33 | for c in cs: 34 | combined += multipler * c_to_int(c, x_width) 35 | multipler *= pow(2, 2*x_width) 36 | return combined 37 | 38 | def int_to_cs(k, x_width, N): 39 | """ 40 | Takes a integer, a width, and the number of complex numbers. 41 | Returns a list of complex numbers. 42 | """ 43 | cs = [] 44 | for n in range(0, N): 45 | kb = k % pow(2, 2*x_width) 46 | cs.append(int_to_c(kb, x_width)) 47 | k = k >> 2*x_width 48 | return cs 49 | 50 | def int_to_c(k, x_width): 51 | """ 52 | Takes an integer and a width and returns a complex number. 53 | """ 54 | ik = k >> x_width 55 | qk = k % pow(2, x_width) 56 | maxint = pow(2, x_width)-1 57 | i = int_to_f(ik, x_width) 58 | q = int_to_f(qk, x_width) 59 | return i + (0+1j)*q 60 | 61 | def f_to_sint(f, x_width, clean1=False): 62 | """ 63 | Takes a float and returns a signed integer. 64 | 65 | If clean1 is True then we scale so that 1 in binary is 66 | 0100000 rather than 0111111. 67 | This allows a multiplication by 1 followed by a down shift 68 | to leave the result unchanged.OB 69 | """ 70 | if f < -1 or f > 1: 71 | raise ValueError("The tap must be between -1 and 1.") 72 | if clean1 is False: 73 | maxint = pow(2, x_width-1)-1 74 | else: 75 | maxint = pow(2, x_width-2) 76 | i = int(round(f*maxint)) 77 | return i 78 | 79 | def sint_to_int(si, width): 80 | """ 81 | Converts a signed integer to a two complement integer. 82 | """ 83 | if si >=0: 84 | return si 85 | else: 86 | return si + pow(2, width) 87 | 88 | def f_to_int(f, width, clean1=False): 89 | return sint_to_int(f_to_sint(f, width, clean1), width) 90 | 91 | def sint_to_f(si, width): 92 | maxint = pow(2, width-1)-1 93 | return float(si)/maxint 94 | 95 | def int_to_sint(i, width): 96 | middleint = pow(2, width)/2 97 | if i >= middleint: 98 | i -= pow(2, width) 99 | return i 100 | 101 | def int_to_f(i, width): 102 | return sint_to_f(int_to_sint(i, width), width) 103 | 104 | def f_to_istr(width, f): 105 | """ 106 | f is between 0 and 1. 107 | If f is 1 we want binary to be 010000000 (maxno). 108 | 109 | Used for generating the twiddle factor module. 110 | """ 111 | if f < 0 or f > 1: 112 | raise ValueError("f must be between 0 and 1") 113 | maxno = pow(2, width-2) 114 | return str(int(round(f * maxno))) 115 | 116 | def is_to_dicts(iis, width): 117 | """ 118 | Converts a list of integer to a list of dictionaries of with 119 | attributes (i, sign, value) 120 | Useful to send to template formatter. 121 | """ 122 | dicts = [] 123 | for i, ii in enumerate(iis): 124 | d = {} 125 | d['i'] = i 126 | if ii >= 0: 127 | d['sign'] = '' 128 | else: 129 | d['sign'] = '-' 130 | d['value'] = abs(ii) 131 | dicts.append(d) 132 | return dicts 133 | 134 | def fs_to_dicts(fs, width, clean1=False): 135 | """ 136 | Converts a list of floats to a list of dictionaries of with 137 | attributes (i, sign, value) 138 | Useful to send to template formatter. 139 | """ 140 | iis = [f_to_sint(f, width, clean1=clean1) for f in fs] 141 | return is_to_dicts(iis, width) 142 | 143 | def cs_to_dicts(cs, width, clean1=False): 144 | """ 145 | Converts a list of complex numbers to a list of dictionaries of with 146 | attributes (i, re_sign, im_sign, re, im) 147 | Useful to send to template formatter. 148 | """ 149 | dicts = [] 150 | for i, c in enumerate(cs): 151 | d = {} 152 | d['i'] = i 153 | if c.real > 0: 154 | d['re_sign'] = '' 155 | else: 156 | d['re_sign'] = '-' 157 | c = -c.real + (0+1j)*c.imag 158 | if c.imag > 0: 159 | d['im_sign'] = '' 160 | else: 161 | d['im_sign'] = '-' 162 | c = c.real - (0+1j)*c.imag 163 | d['re'] = str(f_to_sint(c.real, width/2, clean1=clean1)) 164 | d['im'] = str(f_to_sint(c.imag, width/2, clean1=clean1)) 165 | 166 | dicts.append(d) 167 | return dicts 168 | 169 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fft/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog fft code. 4 | """ 5 | 6 | import cmath 7 | import os 8 | 9 | from fpga_sdrlib import config 10 | from fpga_sdrlib.conversions import cs_to_dicts 11 | from fpga_sdrlib.generate import copyfile, logceil, format_template 12 | 13 | def fft_length_template(pck, fn, dependencies, extraargs={}): 14 | fft_length = extraargs.get('N', None) 15 | if fft_length is None: 16 | raise ValueError("N for stage_to_stage.v is not known.") 17 | template_dict = {'N': fft_length} 18 | assert(fn[-4:] == '.v.t') 19 | ss_fn = fn[:-4] + "_" + str(fft_length) + '.v' 20 | in_fn = os.path.join(config.verilogdir, pck, fn) 21 | out_fn = os.path.join(config.builddir, pck, ss_fn) 22 | out_dir = os.path.join(config.builddir, pck) 23 | if not os.path.exists(out_dir): 24 | os.makedirs(out_dir) 25 | format_template(in_fn, out_fn, 26 | template_dict) 27 | out_extraargs = {} 28 | for d in dependencies: 29 | out_extraargs[d] = extraargs 30 | return out_fn, out_extraargs 31 | 32 | def make_twiddlefactors(pck, fn, dependencies, extraargs={}): 33 | # dependencies is not used 34 | fft_length = extraargs.get('N', None) 35 | width = extraargs.get('width', None) 36 | if fft_length is None: 37 | raise ValueError("N for twiddlefactors.v is not known.") 38 | if width is None: 39 | raise ValueError("width for twidlefactors.v is not known.") 40 | vs = [cmath.exp(-i*2j*cmath.pi/fft_length) for i in range(0, fft_length/2)] 41 | tfs = cs_to_dicts(vs, width, clean1=False) 42 | tf_dict = { 43 | 'N': fft_length, 44 | 'log_N': logceil(fft_length), 45 | 'width': width, 46 | 'tfs': tfs, 47 | } 48 | assert(fn == 'twiddlefactors.v.t') 49 | twiddlefactors_fn = 'twiddlefactors_{0}.v'.format(fft_length) 50 | in_fn = os.path.join(config.verilogdir, pck, fn) 51 | out_fn = os.path.join(config.builddir, pck, twiddlefactors_fn) 52 | out_dir = os.path.join(config.builddir, pck) 53 | if not os.path.exists(out_dir): 54 | os.makedirs(out_dir) 55 | format_template(in_fn, out_fn, 56 | tf_dict) 57 | return out_fn, {} 58 | 59 | blocks = { 60 | # The basic modules. 61 | 'butterfly.v': (('fpgamath/multiply_complex.v', 'message/message_slicer.v', 'message/smaller.v'), copyfile, {}), 62 | 'twiddlefactors.v.t': (None, make_twiddlefactors, {}), 63 | 'mstore.v': (None, copyfile, {}), 64 | 'stage.v': (None, copyfile, {}), 65 | 'buffer_BB_to_stage.v': (None, copyfile, {}), 66 | 'stage_to_stage.v.t': (('butterfly.v', 'twiddlefactors.v.t',), fft_length_template, {}), 67 | 'stage_to_out.v': (None, copyfile, {}), 68 | 'dit_series.v.t': (('stage.v', 'buffer_BB_to_stage.v', 'stage_to_out.v', 69 | 'stage_to_stage.v.t', 'flow/buffer_BB.v'), 70 | fft_length_template, {}), 71 | # qa_contents modules 72 | 'qa_butterfly.v':(('butterfly.v', ), copyfile, {}), 73 | 'qa_stage.v': (('stage.v', 'buffer_BB_to_stage.v', 74 | 'stage_to_out.v', 'flow/buffer_BB.v'), copyfile, {}), 75 | 'qa_stage_to_stage.v.t': (('stage.v', 'buffer_BB_to_stage.v', 76 | 'stage_to_stage.v.t', 'stage_to_out.v', 'flow/buffer_BB.v'), fft_length_template, {}), 77 | 'qa_dit_series.v.t': (('dit_series.v.t',), fft_length_template, {}), 78 | } 79 | 80 | # compatible with running on the B100 81 | compatibles = { 82 | 'butterfly': 83 | ('qa_butterfly.v', 'uhd/qa_wrapper.v'), 84 | 'dit_series': 85 | ('qa_dit_series.v.t', 'uhd/qa_wrapper.v',), 86 | 'stage': 87 | ('qa_stage.v', 'uhd/qa_wrapper.v',), 88 | 'stage_to_stage': 89 | ('qa_stage_to_stage.v.t', 'uhd/qa_wrapper.v',), 90 | } 91 | 92 | # Not compatible with running on the B100 93 | incompatibles = { 94 | 'butterfly_inner': 95 | ('qa_butterfly.v', 'uhd/dut_qa_contents.v'), 96 | 'dit_series_inner': 97 | ('qa_dit_series.v.t', 'uhd/dut_qa_contents.v'), 98 | 'stage_inner': 99 | ('qa_stage.v', 'uhd/dut_qa_contents.v',), 100 | 'stage_to_stage_inner': 101 | ('qa_stage_to_stage.v.t', 'uhd/dut_qa_contents.v',), 102 | } 103 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fft/build.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import cmath 5 | import math 6 | import os 7 | import logging 8 | import shutil 9 | 10 | from jinja2 import Environment, FileSystemLoader 11 | 12 | from fpga_sdrlib import config 13 | from fpga_sdrlib.conversions import cs_to_dicts 14 | from fpga_sdrlib.buildutils import copyfile, format_template, make_define_string 15 | from fpga_sdrlib.math.build import generate_math_files 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | env = Environment(loader=FileSystemLoader( 20 | os.path.join(config.verilogdir, 'fft'))) 21 | 22 | def get_builddir(): 23 | fftbuilddir = os.path.join(config.builddir, 'fft') 24 | if not os.path.exists(fftbuilddir): 25 | os.makedirs(fftbuilddir) 26 | return fftbuilddir 27 | 28 | def generate_dit_files(fft_length, tf_width): 29 | """ 30 | Generate the fft files to perform an fft. 31 | 32 | Args: 33 | fft_length: Length of the FFT. 34 | tf_width: Number of bits in each real number of each twiddle factor. 35 | """ 36 | get_builddir() 37 | inputfiles = generate_math_files() 38 | inputfiles.append(copyfile('fft', 'butterfly.v')) 39 | log_fft_length = math.log(fft_length)/math.log(2) 40 | if log_fft_length != int(log_fft_length): 41 | raise ValueError("fft_length must be a power of two") 42 | log_fft_length = int(log_fft_length) 43 | # Generate the dit.v file 44 | dit_fn = 'dit_{0}'.format(fft_length) 45 | inputfiles.append( 46 | format_template('fft', 'dit.v.t', dit_fn, {'N': fft_length})) 47 | # Generate twiddle factor file. 48 | tf_fn = 'twiddlefactors_{0}'.format(fft_length) 49 | vs = [cmath.exp(-i*2j*cmath.pi/fft_length) for i in range(0, fft_length/2)] 50 | tfs = cs_to_dicts(vs, tf_width*2, clean1=True) 51 | tf_dict = { 52 | 'N': fft_length, 53 | 'log_N': log_fft_length, 54 | 'tf_width': tf_width, 55 | 'tfs': tfs, 56 | } 57 | inputfiles.append( 58 | format_template('fft', 'twiddlefactors.v.t', tf_fn, tf_dict)) 59 | return inputfiles 60 | 61 | def generate_dit_executable(name, fft_length, defines): 62 | log_fft_length = math.log(fft_length)/math.log(2) 63 | if log_fft_length != int(log_fft_length): 64 | raise ValueError("fft_length must be a power of two") 65 | log_fft_length = int(log_fft_length) 66 | get_builddir() 67 | defines['N'] = fft_length 68 | defines['LOG_N'] = log_fft_length 69 | dut_dit_fn = copyfile('fft', 'dut_dit.v') 70 | inputfiles = generate_dit_files(fft_length, defines['WIDTH']/2) 71 | executable = "dit_{name}".format(name=name) 72 | executable = os.path.join(config.builddir, 'fft', executable) 73 | inputfilestr = ' '.join(inputfiles + [dut_dit_fn]) 74 | definestr = make_define_string(defines) 75 | cmd = ("iverilog -o {executable} {definestr} {inputfiles}" 76 | ).format(executable=executable, 77 | definestr=definestr, 78 | inputfiles=inputfilestr) 79 | logger.debug(cmd) 80 | os.system(cmd) 81 | return executable 82 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fft/dit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python implementation of FFT to test verilog against. 3 | """ 4 | import math 5 | import cmath 6 | 7 | def pystage(N, start_index, in_data): 8 | assert(len(in_data) == N) 9 | tfs = [cmath.exp(-i*2j*cmath.pi/N) for i in range(0, N/2)] 10 | tfs = tfs 11 | out_data = [None]*N 12 | # S is number of interleaved transforms. 13 | S = N/2/pow(2,start_index) 14 | for out_addr0 in range(N/2): 15 | # out_addr0 = kS + j 16 | out_addr1 = out_addr0 + N/2 17 | k = out_addr0 // S 18 | j = out_addr0 % S 19 | in_addr0 = 2*k*S + j 20 | in_addr1 = 2*k*S + S + j 21 | tf_addr = k*S 22 | W = tfs[tf_addr] 23 | A = in_data[in_addr0] 24 | B = in_data[in_addr1] 25 | C = A + B*W 26 | D = A - B*W 27 | out_data[out_addr0] = C 28 | out_data[out_addr1] = D 29 | return out_data 30 | 31 | def pyditfft(data): 32 | N = len(data) 33 | log_N = math.log(N)/math.log(2) 34 | if log_N != int(log_N): 35 | raise ValueError("len(data) must be a power of 2") 36 | log_N = int(log_N) 37 | for i in range(log_N): 38 | data = pystage(N, i, data) 39 | return data 40 | 41 | if __name__ == '__main__': 42 | import random 43 | n_data = 16 44 | data = [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 45 | from numpy import fft 46 | ee = fft.fft(data) 47 | rr = pyditfft(data) 48 | assert(len(ee) == len(rr)) 49 | tol = 1e-6 50 | for e, r in zip(ee, rr): 51 | assert(abs(e-r) < tol) 52 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fft/qa_dit.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | """ 5 | MyHDL Test Bench to check the vericode FFT. 6 | """ 7 | 8 | import os 9 | import random 10 | import unittest 11 | import logging 12 | 13 | from numpy import fft 14 | 15 | from fpga_sdrlib.conversions import c_to_int, cs_to_int, int_to_c, int_to_cs 16 | from fpga_sdrlib.testbench import TestBenchIcarus 17 | from fpga_sdrlib.fft.build import generate_dit_executable 18 | from fpga_sdrlib import config 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | class DITTestBenchIcarus(TestBenchIcarus): 23 | """ 24 | Helper class for doing testing. 25 | 26 | Args: 27 | name: A name to use with for generated files. 28 | fft_length: The fft length (must be a power of 2) 29 | in_samples: A list of complex points to send. 30 | sendnth: Send an input on every `sendnth` clock cycle. 31 | in_ms: A list of the meta data to send. 32 | defines: Macro definitions (constants) to use in verilog code. 33 | """ 34 | 35 | extra_signal_names = ['first'] 36 | 37 | def __init__(self, name, fft_length, in_samples, sendnth=config.default_sendnth, 38 | in_ms=None, start_msgs=None, defines=config.default_defines): 39 | super(DITTestBenchIcarus, self).__init__(name, in_samples, sendnth, 40 | in_ms, defines=defines) 41 | self.fft_length = fft_length 42 | 43 | def prepare(self): 44 | self.executable = generate_dit_executable(self.name, self.fft_length, self.defines) 45 | 46 | class TestFFT(unittest.TestCase): 47 | 48 | def setUp(self): 49 | rg = random.Random(0) 50 | self.myrand = rg.random 51 | self.myrandint = rg.randint 52 | 53 | def test_sixteen(self): 54 | width = 16 55 | nlog2 = 4 56 | # Number of FFT to perform 57 | N_data_sets = 4 58 | # How often to send input. 59 | # For large FFTs this must be larger since the speed scales as NlogN. 60 | # Otherwise we get an overflow error. 61 | sendnth = 10 62 | self.random_template(nlog2, width, N_data_sets, sendnth) 63 | 64 | def test_four(self): 65 | width = 16 66 | nlog2 = 2 67 | N_data_sets = 4 68 | sendnth = 2 69 | self.random_template(nlog2, width, N_data_sets, sendnth) 70 | 71 | def test_overflow(self): 72 | width = 16 73 | nlog2 = 4 74 | N_data_sets = 4 75 | sendnth = 1 76 | # Check that this raises an overflow error 77 | self.assertRaises(StandardError, self.random_template, (nlog2, width, N_data_sets, sendnth)) 78 | 79 | def random_template(self, nlog2, width, N_data_sets, sendnth): 80 | """ 81 | Test the DUT with a random complex stream. 82 | """ 83 | N = pow(2, nlog2) 84 | # Approx many steps we'll need. 85 | steps_rqd = 2*N_data_sets*int(40.0 / 8 / 3 * nlog2 * N) 86 | # Generate some random input. 87 | data_sets = [] 88 | data = [] 89 | for i in range(0, N_data_sets): 90 | nd = [self.myrand()*2-1 + self.myrand()*2j-1jfor x in range(N)] 91 | data_sets.append(nd) 92 | data += nd 93 | mwidth = 3 94 | ms = [self.myrandint(0, pow(2, mwidth)-1) for d in data] 95 | # Create, setup and simulate the test bench. 96 | defines = config.updated_defines({"DEBUG": False, 97 | "WIDTH": width, 98 | "MWIDTH": mwidth}) 99 | tb = DITTestBenchIcarus('standard', N, data, sendnth, ms, defines=defines) 100 | tb.prepare() 101 | tb.run(steps_rqd) 102 | 103 | # Confirm that our data is correct. 104 | self.assertEqual(len(tb.out_samples), len(data)) 105 | rffts = [tb.out_samples[N*i: N*(i+1)] for i in range(N_data_sets)] 106 | # Compare the FFT to that generated by numpy 107 | # The FFT from our DUT is divided by N to prevent overflow so we do the 108 | # same to the numpy output. 109 | effts = [[x/N for x in fft.fft(data_set)] for data_set in data_sets] 110 | i = 0 111 | self.assertEqual(len(rffts), len(effts)) 112 | max_delta = 0.02 113 | for rfft, efft in zip(rffts, effts): 114 | self.assertEqual(len(rfft), len(efft)) 115 | for e,r in zip(efft, rfft): 116 | delta = abs(r-e) 117 | self.assertTrue(delta < max_delta) 118 | # Compare ms 119 | for r, e in zip(tb.out_ms, ms): 120 | self.assertEqual(r, e) 121 | 122 | if __name__ == '__main__': 123 | config.setup_logging(logging.DEBUG) 124 | unittest.main() 125 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fft/qa_fpga_dit.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | """ 5 | QA to check the vericode DIT-FFT on the FPGA. 6 | """ 7 | 8 | import os 9 | import random 10 | import unittest 11 | import logging 12 | import math 13 | import itertools 14 | 15 | from numpy import fft 16 | 17 | from gnuradio import uhd, gr 18 | 19 | from fpga_sdrlib.fft.build import generate, make_qa_dit 20 | from fpga_sdrlib import config 21 | from fpga_sdrlib import b100 22 | 23 | class TestDit(unittest.TestCase): 24 | """ 25 | Test the verilog DIT-FFT on B100. 26 | """ 27 | 28 | def setUp(self): 29 | # Width of a complex number 30 | self.width = 32 31 | self.mwidth = 1 32 | self.n = 8 33 | if False: 34 | executable, dit_inputfiles = generate( 35 | 'xilinx', self.n, self.width, self.mwidth) 36 | make_qa_dit(self.n, self.width, self.mwidth) 37 | qa_dit_fn = os.path.join(config.builddir, 'fft', 'qa_dit.v') 38 | b100.make_make('dit', dit_inputfiles + [qa_dit_fn]) 39 | b100.synthesise('dit') 40 | b100.copy_image('dit') 41 | 42 | 43 | def tearDown(self): 44 | pass 45 | 46 | def test_dit(self): 47 | """ 48 | Test a DIT_FFT. 49 | """ 50 | # The amount of data to send 51 | self.n_data = 100 52 | # Random number generator 53 | rg = random.Random(0) 54 | self.myrand = rg.random 55 | # Get the input data 56 | self.data = [(self.myrand()*2-1) + 1j*(self.myrand()*2-1) for i in range(self.n_data)] 57 | self.data = [0]* 200 + [1] + [0]*200 58 | # Create the top signal processing flow graph 59 | tb = gr.top_block() 60 | # Create the path for data to the USRP 61 | stream_args = uhd.stream_args(cpu_format='fc32', channels=range(1)) 62 | src = gr.vector_source_c(self.data) 63 | to_usrp = uhd.usrp_sink(device_addr='', stream_args=stream_args) 64 | tb.connect(src, to_usrp) 65 | # Create the path from the USRP 66 | from_usrp = uhd.usrp_source(device_addr='', stream_args=stream_args) 67 | n_receive = 100000 68 | head = gr.head(gr.sizeof_gr_complex, n_receive) 69 | snk = gr.vector_sink_c() 70 | tb.connect(from_usrp, head, snk) 71 | # Run the flow graph 72 | tb.run() 73 | # Work out the data offset 74 | r_data = snk.data() 75 | first_i = None 76 | for i, d in enumerate(r_data): 77 | if d: 78 | first_i = i 79 | break 80 | if first_i is None: 81 | raise StandardError("No data received. Try increasing n_receive.") 82 | if first_i + self.n_data > n_receive: 83 | raise StandardError("Increase n_receive.") 84 | # Work out expected data 85 | x_data = self.data + [0] * self.n 86 | ffteds = [fft.fft(self.data[i:i+self.n]) for i in range(0, len(self.data), self.n)] 87 | e_data = [] 88 | for ffted in ffteds: 89 | for d in ffted: 90 | e_data.append(d) 91 | r_data = r_data[first_i:first_i+len(e_data)] 92 | if False: 93 | z_data = [0]*8 + self.data 94 | print('********') 95 | for i in range(0, 16): 96 | d = fft.fft(z_data[i:i+8]) 97 | d = [x/self.n for x in d] 98 | print(d[0:2]) 99 | if abs(d[0] - r_data[0]) < 1e-3: 100 | print('hello') 101 | print('********') 102 | print(r_data[0:2]) 103 | print(r_data[8:10]) 104 | print([d*self.n for d in r_data]) 105 | for i in range(8): 106 | print(list(fft.fft([0]*i + [1] + [0]*(7-i)))) 107 | # And finally compare the data 108 | for e, r in zip(e_data, r_data): 109 | self.assertAlmostEqual(e.real, r.real, 3) 110 | self.assertAlmostEqual(e.imag, r.imag, 3) 111 | 112 | 113 | 114 | if __name__ == '__main__': 115 | config.setup_logging(logging.DEBUG) 116 | unittest.main() 117 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/flow/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog message stream code. 4 | """ 5 | 6 | from fpga_sdrlib.generate import copyfile 7 | 8 | blocks = { 9 | # The basic modules. 10 | 'split.v': (None, copyfile, {}), 11 | # Test module. 12 | 'dut_split.v': (None, copyfile, {}), 13 | # A qa_wrapper that returns the first output from the splitter. 14 | 'qa_split.v': (None, copyfile, {}), 15 | # A buffer. Can read every second clock cycle. 16 | 'buffer_AA.v': (None, copyfile, {}), 17 | # A buffer. Can read every clock cycle. 18 | 'buffer_BB.v': (None, copyfile, {}), 19 | # A qa_wrapper for buffer_AA 20 | 'qa_buffer_AA.v': (('buffer_AA.v',), copyfile, {}), 21 | # A qa_wrapper for buffer_AA that empties the buffer in bursts. 22 | 'qa_buffer_AA_burst.v': (('buffer_AA.v',), copyfile, {}), 23 | # A qa_wrapper for buffer_AA 24 | 'qa_buffer_BB.v': (('buffer_BB.v',), copyfile, {}), 25 | } 26 | 27 | # compatible with running on the B100 28 | compatibles = { 29 | 'split_return_one': 30 | ('qa_split.v', 'split.v'), 31 | 'buffer_AA': 32 | ('qa_buffer_AA.v', ), 33 | 'buffer_AA_burst': 34 | ('qa_buffer_AA_burst.v', ), 35 | 'buffer_BB': 36 | ('qa_buffer_BB.v', ), 37 | } 38 | 39 | # Not compatible with running on the B100 40 | incompatibles = { 41 | 'split': 42 | ('split.v', 'dut_split.v'), 43 | } 44 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/flow/qa_flow.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import os 5 | import random 6 | import unittest 7 | import logging 8 | import shutil 9 | 10 | from fpga_sdrlib.generate import logceil 11 | from fpga_sdrlib import config, b100, buildutils 12 | from fpga_sdrlib.testbench import TestBenchB100, TestBenchIcarusOuter 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | class TestSplit(unittest.TestCase): 17 | 18 | def test_one(self): 19 | """ 20 | Test the split module. 21 | """ 22 | width = 32 23 | sendnth = 4 24 | maxint = pow(2, width)-1 25 | n_data = 100 26 | n_streams = 3 27 | data = [random.randint(0, maxint) for d in range(n_data)] 28 | # Work out what the expected result is. 29 | expected_data = [] 30 | for ds in [data[n_streams*i:n_streams*(i+1)] for i in range(n_data/n_streams)]: 31 | e_d = 0 32 | f = 1 33 | for d in ds: 34 | e_d += d*f 35 | f *= pow(2, width) 36 | expected_data.append(e_d) 37 | # How many steps are required to simulate the data. 38 | steps_rqd = n_data * sendnth * 2 + 1000 39 | # Create, setup and simulate the test bench. 40 | defines = config.updated_defines( 41 | {'N_OUT_STREAMS': n_streams, 42 | 'LOG_N_OUT_STREAMS': logceil(n_streams), 43 | 'WIDTH': width, 44 | }) 45 | executable = buildutils.generate_icarus_executable( 46 | 'flow', 'split', '-test', defines) 47 | tb = TestBenchIcarusOuter(executable, in_raw=data, width=width, 48 | output_msgs=False) 49 | tb.run(steps_rqd) 50 | # Confirm that our data is correct. 51 | self.assertEqual(len(tb.out_raw), len(expected_data)) 52 | for r, e in zip(tb.out_raw, expected_data): 53 | self.assertEqual(e, r) 54 | 55 | def test_return_one(self): 56 | """ 57 | Test the split module. 58 | """ 59 | width = config.default_width 60 | sendnth = 4 61 | maxint = pow(2, width)-1 62 | n_data = 100 63 | n_streams = 2 64 | data = [random.randint(0, maxint) for d in range(n_data)] 65 | # Work out what the expected result is. 66 | # We don't know which stream will be returned. 67 | expected_data = data[::2] 68 | alt_expected_data = data[1::2] 69 | # How many steps are required to simulate the data. 70 | steps_rqd = n_data * sendnth * 2 + 1000 71 | # Create, setup and simulate the test bench. 72 | defines = config.updated_defines( 73 | {'WIDTH': width, 74 | }) 75 | executable = buildutils.generate_icarus_executable( 76 | 'flow', 'split_return_one', '-test', defines=defines) 77 | fpgaimage = buildutils.generate_B100_image( 78 | 'flow', 'split_return_one', '-test', defines=defines) 79 | tb_icarus = TestBenchIcarusOuter(executable, in_raw=data, 80 | output_msgs=False) 81 | tb_b100 = TestBenchB100(fpgaimage, in_raw=data, output_msgs=False) 82 | for tb, steps in ( 83 | (tb_icarus, steps_rqd), 84 | (tb_b100, 100000), 85 | ): 86 | tb.run(steps) 87 | # Confirm that our data is correct. 88 | stream = None 89 | self.assertEqual(len(tb.out_raw), len(expected_data)) 90 | for r, e, a in zip(tb.out_raw, expected_data, alt_expected_data): 91 | if stream is None: 92 | if (r != e): 93 | stream = 2 94 | else: 95 | stream = 1 96 | if stream == 1: 97 | self.assertEqual(e, r) 98 | else: 99 | self.assertEqual(a, r) 100 | 101 | class TestBuffer(unittest.TestCase): 102 | 103 | def test_one(self): 104 | """ 105 | Test the buffer_AA and buffer_BB module. 106 | """ 107 | width = config.default_width 108 | sendnth = 1 109 | maxint = pow(2, width)-1 110 | buffer_length = 32 111 | n_data = 100 112 | data = [random.randint(1, maxint) for d in range(n_data)] 113 | # How many steps are required to simulate the data. 114 | steps_rqd = n_data * sendnth * 2 + 1000 115 | # Create, setup and simulate the test bench. 116 | defines = config.updated_defines( 117 | {'WIDTH': width, 118 | 'BUFFER_LENGTH': buffer_length, 119 | 'LOG_BUFFER_LENGTH': logceil(buffer_length), 120 | 'WRITEERRORCODE': 666, 121 | 'READERRORCODE': 777, 122 | }) 123 | executableAA = buildutils.generate_icarus_executable( 124 | 'flow', 'buffer_AA', '-test', defines=defines) 125 | fpgaimageAA = buildutils.generate_B100_image( 126 | 'flow', 'buffer_AA', '-test', defines=defines) 127 | executableBB = buildutils.generate_icarus_executable( 128 | 'flow', 'buffer_BB', '-test', defines=defines) 129 | fpgaimageBB = buildutils.generate_B100_image( 130 | 'flow', 'buffer_BB', '-test', defines=defines) 131 | tb_icarusAA = TestBenchIcarusOuter(executableAA, in_raw=data, 132 | output_msgs=False) 133 | tb_b100AA = TestBenchB100(fpgaimageAA, in_raw=data, output_msgs=False) 134 | tb_icarusBB = TestBenchIcarusOuter(executableBB, in_raw=data, 135 | output_msgs=False) 136 | tb_b100BB = TestBenchB100(fpgaimageBB, in_raw=data, output_msgs=False) 137 | for tb, steps in ( 138 | (tb_icarusAA, steps_rqd), 139 | (tb_b100AA, 100000), 140 | (tb_icarusBB, steps_rqd), 141 | (tb_b100BB, 100000), 142 | ): 143 | tb.run(steps) 144 | # Confirm that our data is correct. 145 | stream = None 146 | self.assertEqual(len(tb.out_raw), len(data)) 147 | for r, e in zip(tb.out_raw, data): 148 | self.assertEqual(e, r) 149 | 150 | def test_bursts(self): 151 | """ 152 | Test the buffer_AA module. 153 | """ 154 | width = config.default_width 155 | sendnth = 1 156 | maxint = pow(2, width)-1 157 | buffer_length = 32 158 | burst_length = 4 159 | n_data = 100 160 | data = [random.randint(1, maxint) for d in range(n_data)] 161 | # How many steps are required to simulate the data. 162 | steps_rqd = n_data * sendnth * 2 + 1000 163 | # Create, setup and simulate the test bench. 164 | defines = config.updated_defines( 165 | {'WIDTH': width, 166 | 'BUFFER_LENGTH': buffer_length, 167 | 'LOG_BUFFER_LENGTH': logceil(buffer_length), 168 | 'LOG_BURST_LENGTH': logceil(burst_length), 169 | 'WRITEERRORCODE': 666, 170 | 'READERRORCODE': 777, 171 | }) 172 | executable = buildutils.generate_icarus_executable( 173 | 'flow', 'buffer_AA_burst', '-test', defines=defines) 174 | fpgaimage = buildutils.generate_B100_image( 175 | 'flow', 'buffer_AA_burst', '-test', defines=defines) 176 | tb_icarus = TestBenchIcarusOuter(executable, in_raw=data, 177 | output_msgs=False) 178 | tb_b100 = TestBenchB100(fpgaimage, in_raw=data, output_msgs=False) 179 | for tb, steps in ( 180 | (tb_icarus, steps_rqd), 181 | (tb_b100, 100000), 182 | ): 183 | tb.run(steps) 184 | # Confirm that our data is correct. 185 | stream = None 186 | self.assertEqual(len(tb.out_raw), len(data)) 187 | for r, e in zip(tb.out_raw, data): 188 | self.assertEqual(e, r) 189 | 190 | if __name__ == '__main__': 191 | config.setup_logging(logging.DEBUG) 192 | #suite = unittest.TestLoader().loadTestsFromTestCase(TestSplit) 193 | #suite = unittest.TestLoader().loadTestsFromTestCase(TestBuffer) 194 | #unittest.TextTestRunner(verbosity=2).run(suite) 195 | unittest.main() 196 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/flter/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog filter code. 4 | """ 5 | 6 | import os 7 | import math 8 | 9 | from fpga_sdrlib import config 10 | from fpga_sdrlib.generate import copyfile, format_template 11 | 12 | def make_filter(pck, fn, dependencies, extraargs={}): 13 | """ 14 | Generate filter_X.v from filter.v 15 | 16 | This is done so that we can convert the 2D array tapvalues to 17 | a 1D array to pass to summult. 18 | """ 19 | # dependencies is not used 20 | length = extraargs.get('summult_length', None) 21 | if length is None: 22 | raise ValueError("Length for filter.v is not known.") 23 | log_length = int(math.ceil(math.log(length)/math.log(2))) 24 | # Generate filter file 25 | assert(fn == 'filter.v.t') 26 | summult_fn = 'filter_{0}.v'.format(length) 27 | in_fn = os.path.join(config.verilogdir, pck, fn) 28 | out_fn = os.path.join(config.builddir, pck, summult_fn) 29 | out_dir = os.path.join(config.builddir, pck) 30 | if not os.path.exists(out_dir): 31 | os.makedirs(out_dir) 32 | tapvalues_1D = ["tapvalues[{0}]".format(i) for i in reversed(range(length))] 33 | tapvalues_1D = "{" + ", ".join(tapvalues_1D) + "}" 34 | format_template(in_fn, out_fn, {'tapvalues_1D': tapvalues_1D}) 35 | return out_fn, {'summult.v.t': extraargs} 36 | 37 | 38 | def make_summult(pck, fn, dependencies, extraargs={}): 39 | # dependencies is not used 40 | length = extraargs.get('summult_length', None) 41 | if length is None: 42 | raise ValueError("Length for summult.v is not known.") 43 | log_length = int(math.ceil(math.log(length)/math.log(2))) 44 | # Generate summult file 45 | assert(fn == 'summult.v.t') 46 | summult_fn = 'summult_{0}.v'.format(length) 47 | in_fn = os.path.join(config.verilogdir, pck, fn) 48 | out_fn = os.path.join(config.builddir, pck, summult_fn) 49 | out_dir = os.path.join(config.builddir, pck) 50 | if not os.path.exists(out_dir): 51 | os.makedirs(out_dir) 52 | real_sum = ["x_re_y[{0}]".format(i) for i in range(length)] 53 | real_sum = " + ".join(real_sum) 54 | imag_sum = ["x_im_y[{0}]".format(i) for i in range(length)] 55 | imag_sum = " + ".join(imag_sum) 56 | format_template(in_fn, out_fn, 57 | {'real_sum': real_sum, 58 | 'imag_sum': imag_sum}) 59 | return out_fn, {} 60 | 61 | blocks = { 62 | # The basic modules. 63 | 'filter.v.t': (('summult.v.t',), make_filter, {}), 64 | 'summult.v.t': (('fpgamath/multiply.v', ), make_summult, {}), 65 | 'filterbank.v': (('filter.v.t', 'flow/buffer_BB.v', ), copyfile, {}), 66 | # A qa_contents modules 67 | 'qa_filter.v': (('filter.v.t',), copyfile, {}), 68 | 'qa_filterbank.v': (('filterbank.v',), copyfile, {}), 69 | } 70 | 71 | # compatible with running on the B100 72 | compatibles = { 73 | 'filter': 74 | ('qa_filter.v', 'uhd/qa_wrapper.v',), 75 | 'filterbank': 76 | ('qa_filterbank.v', 'uhd/qa_wrapper.v',), 77 | } 78 | 79 | # Not compatible with running on the B100 80 | incompatibles = { 81 | 'filter_inner': 82 | ('qa_filter.v', 'uhd/dut_qa_contents.v'), 83 | 'filterbank_inner': 84 | ('qa_filterbank.v', 'uhd/dut_qa_contents.v',), 85 | } 86 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/flter/qa_flter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import os 5 | import random 6 | import unittest 7 | import logging 8 | import shutil 9 | 10 | from fpga_sdrlib.message import msg_utils 11 | from fpga_sdrlib.conversions import f_to_int 12 | from fpga_sdrlib.generate import logceil 13 | from fpga_sdrlib import config, b100, buildutils 14 | from fpga_sdrlib.testbench import TestBenchB100, TestBenchIcarusInner, TestBenchIcarusOuter 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | def convolve(data, taps): 19 | out = [] 20 | data = [0]*(len(taps)-1) + data 21 | for i in range(len(taps)-1, len(data)): 22 | v = 0 23 | for j in range(len(taps)): 24 | v += data[i-j]*taps[j] 25 | out.append(v) 26 | return out 27 | 28 | def taps_to_start_msgs(taps, width, target): 29 | contents = [f_to_int(tap, width, clean1=True) for tap in taps] 30 | packet = msg_utils.packet_from_content(contents, config.msg_length_width, 31 | config.msg_width, target) 32 | return packet 33 | 34 | def prune_zeros(xs): 35 | start_index = None 36 | stop_index = None 37 | for i, x in enumerate(xs): 38 | if x != 0: 39 | if start_index is None: 40 | start_index = i 41 | stop_index = i 42 | if start_index is None: 43 | return [] 44 | else: 45 | return xs[start_index:stop_index+1] 46 | 47 | class TestFilter(unittest.TestCase): 48 | 49 | def test_one(self): 50 | """ 51 | Test the filter module. 52 | """ 53 | width = config.default_width 54 | sendnth = config.default_sendnth 55 | # Changing filter_length will require resynthesis. 56 | filter_length = 4 57 | taps = [random.random()*2-1 for i in range(filter_length)] 58 | total = sum([abs(t) for t in taps]) 59 | taps = [t/total for t in taps] 60 | # Arguments used for producing verilog from templates. 61 | extraargs = {'summult_length': filter_length,} 62 | # Amount of data to send. 63 | n_data = 10 64 | # Define the input 65 | in_samples = [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 66 | in_samples += [0]*(filter_length-1) 67 | steps_rqd = len(in_samples)*sendnth + 100 68 | # Define meta data 69 | mwidth = 1 70 | in_ms = [random.randint(0, pow(2,mwidth)-1) for d in in_samples] 71 | expected = convolve(in_samples, taps) 72 | steps_rqd = n_data * sendnth * 2 + 1000 73 | filter_id = 123 74 | # Create, setup and simulate the test bench. 75 | defines = config.updated_defines( 76 | {'WIDTH': width, 77 | 'FILTER_LENGTH': filter_length, 78 | 'FILTER_ID': filter_id, 79 | }) 80 | executable_inner = buildutils.generate_icarus_executable( 81 | 'flter', 'filter_inner', '-test', defines=defines, extraargs=extraargs) 82 | executable_outer = buildutils.generate_icarus_executable( 83 | 'flter', 'filter', '-test', defines=defines, extraargs=extraargs) 84 | fpgaimage = buildutils.generate_B100_image( 85 | 'flter', 'filter', '-test', defines=defines, 86 | extraargs=extraargs) 87 | start_msgs = taps_to_start_msgs(taps, defines['WIDTH']/2, filter_id) 88 | tb_icarus_inner = TestBenchIcarusInner(executable_inner, in_samples, in_ms, start_msgs) 89 | tb_icarus_outer = TestBenchIcarusOuter(executable_outer, in_samples, start_msgs) 90 | tb_b100 = TestBenchB100(fpgaimage, in_samples, start_msgs) 91 | for tb, steps in ( 92 | (tb_icarus_inner, steps_rqd), 93 | (tb_icarus_outer, steps_rqd), 94 | (tb_b100, 100000), 95 | ): 96 | tb.run(steps) 97 | # Confirm that our data is correct. 98 | self.assertEqual(len(tb.out_samples), len(expected)) 99 | for r, e in zip(tb.out_samples, expected): 100 | self.assertAlmostEqual(e, r, 3) 101 | 102 | class TestFilterBank(unittest.TestCase): 103 | 104 | def test_one(self): 105 | """ 106 | Test the filterbank module. 107 | """ 108 | width = config.default_width 109 | sendnth = config.default_sendnth 110 | # Changing filter_length will require resynthesis. 111 | n_filters = 3 112 | filter_length = 3 113 | all_taps = [] 114 | combined_taps = [] 115 | for n in range(n_filters): 116 | taps = [random.random()*2-1 for i in range(filter_length)] 117 | total = sum([abs(t) for t in taps]) 118 | taps = [t/total for t in taps] 119 | all_taps.append(taps) 120 | combined_taps.extend(taps) 121 | # Arguments used for producing verilog from templates. 122 | extraargs = {'summult_length': filter_length,} 123 | # Amount of data to send. 124 | n_data = 30 125 | # Define the input 126 | in_samples = [0]*filter_length*n_filters*2 127 | in_samples += [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 128 | in_samples += [0]*(filter_length-1)*n_filters 129 | steps_rqd = len(in_samples)*sendnth + 100 130 | # Define meta data 131 | mwidth = 1 132 | in_ms = [random.randint(0, pow(2,mwidth)-1) for d in in_samples] 133 | possible_expected = [] 134 | for m in range(n_filters): 135 | shifted_taps = all_taps[m:] + all_taps[:m] 136 | expected_outputs = [] 137 | for n in range(n_filters): 138 | filter_inputs = in_samples[n::n_filters] 139 | convolved = convolve(filter_inputs, shifted_taps[n]) 140 | expected_outputs.append(convolved) 141 | expected = [] 142 | for eo in zip(*expected_outputs): 143 | expected.extend(eo) 144 | possible_expected.append(expected) 145 | steps_rqd = n_data * sendnth * 2 + 1000 146 | # Create, setup and simulate the test bench. 147 | filter_id = 123 148 | defines = config.updated_defines( 149 | {'WIDTH': width, 150 | 'FILTER_LENGTH': filter_length, 151 | 'FILTERBANK_ID': filter_id, 152 | 'N_FILTERS': n_filters, 153 | 'FILTERBANK_MSG_BUFFER_LENGTH': 128, 154 | }) 155 | executable_inner = buildutils.generate_icarus_executable( 156 | 'flter', 'filterbank_inner', '-test', defines=defines, extraargs=extraargs) 157 | executable_outer = buildutils.generate_icarus_executable( 158 | 'flter', 'filterbank', '-test', defines=defines, extraargs=extraargs) 159 | fpgaimage = buildutils.generate_B100_image( 160 | 'flter', 'filterbank', '-test', defines=defines, 161 | extraargs=extraargs) 162 | start_msgs = taps_to_start_msgs(combined_taps, defines['WIDTH']/2, filter_id) 163 | tb_icarus_inner = TestBenchIcarusInner(executable_inner, in_samples, in_ms, start_msgs) 164 | tb_icarus_outer = TestBenchIcarusOuter(executable_outer, in_samples, start_msgs) 165 | tb_b100 = TestBenchB100(fpgaimage, in_samples, start_msgs) 166 | for tb, steps in ( 167 | (tb_icarus_inner, steps_rqd), 168 | (tb_icarus_outer, steps_rqd), 169 | (tb_b100, 100000), 170 | ): 171 | tb.run(steps) 172 | # Confirm that our data is correct. 173 | received = prune_zeros(tb.out_samples) 174 | tol = 0.001 175 | matched_once = False 176 | for expected in possible_expected: 177 | expected = prune_zeros(expected) 178 | matches = True 179 | if (len(received) != len(expected)): 180 | matches = False 181 | else: 182 | for r, e in zip(received, expected): 183 | if (abs(r-e) > tol): 184 | matches = False 185 | break 186 | if matches: 187 | matched_once = True 188 | self.assertTrue(matched_once) 189 | 190 | if __name__ == '__main__': 191 | config.setup_logging(logging.DEBUG) 192 | #suite = unittest.TestLoader().loadTestsFromTestCase(TestFilterBank) 193 | #unittest.TextTestRunner(verbosity=2).run(suite) 194 | unittest.main() 195 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fpgamath/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog math code. 4 | """ 5 | 6 | from fpga_sdrlib.generate import copyfile 7 | 8 | blocks = { 9 | # The basic modules. 10 | 'log.v': (None, copyfile, {}), 11 | 'multiply.v': (None, copyfile, {}), 12 | 'multiply_complex.v': (('multiply.v',), copyfile, {}), 13 | # qa_contents modules. 14 | 'qa_multiply.v': (('multiply.v',), copyfile, {}), 15 | 'qa_multiply_complex.v': (('multiply_complex.v',), copyfile, {}), 16 | } 17 | 18 | # compatible with running on the B100 19 | compatibles = { 20 | 'multiply': 21 | ('qa_multiply.v', 'uhd/qa_wrapper.v'), 22 | 'multiply_complex': 23 | ('qa_multiply_complex.v', 'uhd/qa_wrapper.v'), 24 | } 25 | 26 | # Not compatible with running on the B100 27 | incompatibles = { 28 | 'multiply_inner': 29 | ('qa_multiply.v', 'uhd/dut_qa_contents.v'), 30 | 'multiply_complex_inner': 31 | ('qa_multiply_complex.v', 'uhd/dut_qa_contents.v'), 32 | } 33 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/fpgamath/qa_fpgamath.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import os 5 | import random 6 | import unittest 7 | import logging 8 | import shutil 9 | 10 | from fpga_sdrlib.message import msg_utils 11 | from fpga_sdrlib.conversions import f_to_int 12 | from fpga_sdrlib.generate import logceil 13 | from fpga_sdrlib import config, b100, buildutils 14 | from fpga_sdrlib.testbench import TestBenchB100, TestBenchIcarusInner, TestBenchIcarusOuter 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | class TestMultiply(unittest.TestCase): 19 | 20 | def test_one(self): 21 | """ 22 | Test the multiply module. 23 | """ 24 | sendnth = 2 25 | n_data = 1000 26 | in_samples = [] 27 | expected = [] 28 | # We send samples a + bj and expected returned 0 + abj. 29 | in_samples = [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 30 | expected = [c.real*c.imag*1j for c in in_samples] 31 | steps_rqd = len(in_samples)*sendnth*2 + 100 32 | executable_inner = buildutils.generate_icarus_executable( 33 | 'fpgamath', 'multiply_inner', '-test',) 34 | executable_outer = buildutils.generate_icarus_executable( 35 | 'fpgamath', 'multiply', '-test',) 36 | fpgaimage = buildutils.generate_B100_image( 37 | 'fpgamath', 'multiply', '-test') 38 | tb_icarus_inner = TestBenchIcarusInner(executable_inner, in_samples, 39 | sendnth=sendnth) 40 | tb_icarus_outer = TestBenchIcarusOuter(executable_outer, in_samples, 41 | sendnth=sendnth) 42 | tb_b100 = TestBenchB100(fpgaimage, in_samples) 43 | for tb, steps, check_ms in ( 44 | (tb_icarus_inner, steps_rqd, True), 45 | (tb_icarus_outer, steps_rqd, False), 46 | (tb_b100, 100000, False), 47 | ): 48 | tb.run(steps) 49 | # Confirm that our data is correct. 50 | self.assertEqual(len(tb.out_samples), len(expected)) 51 | for r, e in zip(tb.out_samples, expected): 52 | self.assertAlmostEqual(e, r, 3) 53 | 54 | class TestMultiplyComplex(unittest.TestCase): 55 | 56 | def test_one(self): 57 | """ 58 | Test the multiply_complex module. 59 | """ 60 | sendnth = 2 61 | n_data = 1000 62 | in_samples = [] 63 | expected = [] 64 | xs = [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 65 | ys = [random.random()*2-1 + random.random()*2j-1j for i in range(n_data)] 66 | for x, y in zip(xs, ys): 67 | in_samples.append(x) 68 | in_samples.append(y) 69 | z = x*y/2 70 | # We divide by two since multiplying two complex numbers in range (-1,-1) to (1,1) 71 | # produces a result in range (-2, -2) to (2, 2). 72 | expected.append(z) 73 | steps_rqd = len(in_samples)*sendnth*2 + 100 74 | executable_inner = buildutils.generate_icarus_executable( 75 | 'fpgamath', 'multiply_complex_inner', '-test',) 76 | executable_outer = buildutils.generate_icarus_executable( 77 | 'fpgamath', 'multiply_complex', '-test',) 78 | fpgaimage = buildutils.generate_B100_image( 79 | 'fpgamath', 'multiply_complex', '-test') 80 | tb_icarus_inner = TestBenchIcarusInner(executable_inner, in_samples, 81 | sendnth=sendnth) 82 | tb_icarus_outer = TestBenchIcarusOuter(executable_outer, in_samples, 83 | sendnth=sendnth) 84 | tb_b100 = TestBenchB100(fpgaimage, in_samples) 85 | for tb, steps, check_ms in ( 86 | (tb_icarus_inner, steps_rqd, True), 87 | (tb_icarus_outer, steps_rqd, False), 88 | (tb_b100, 100000, False), 89 | ): 90 | tb.run(steps) 91 | # Confirm that our data is correct. 92 | self.assertEqual(len(tb.out_samples), len(expected)) 93 | for r, e in zip(tb.out_samples, expected): 94 | self.assertAlmostEqual(e, r, 3) 95 | 96 | if __name__ == '__main__': 97 | config.setup_logging(logging.DEBUG) 98 | #suite = unittest.TestLoader().loadTestsFromTestCase(TestMultiply) 99 | #unittest.TextTestRunner(verbosity=2).run(suite) 100 | unittest.main() 101 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/generate.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os 3 | import math 4 | 5 | from jinja2 import Environment, FileSystemLoader 6 | 7 | from fpga_sdrlib import config 8 | 9 | def logceil(n): 10 | val = int(math.ceil(float(math.log(n))/math.log(2))) 11 | # To keep things simple never return 0. 12 | # Declaring reg with 0 length is not legal. 13 | if val == 0: 14 | val = 1 15 | return val 16 | 17 | def copyfile(directory, name, dependencies, extraargs={}): 18 | in_fn = os.path.join(config.verilogdir, directory, name) 19 | out_fn = os.path.join(config.builddir, directory, name) 20 | out_dir = os.path.join(config.builddir, directory) 21 | if not os.path.exists(out_dir): 22 | os.makedirs(out_dir) 23 | shutil.copyfile(in_fn, out_fn) 24 | dep_extraargs = dict([(d, extraargs) for d in dependencies]) 25 | return out_fn, dep_extraargs 26 | 27 | def format_template(template_fn, output_fn, template_args): 28 | """ 29 | Formats a template. 30 | """ 31 | env = Environment(loader=FileSystemLoader(config.verilogdir)) 32 | template = env.get_template(os.path.relpath(template_fn, config.verilogdir)) 33 | f_out = open(output_fn, 'w') 34 | f_out.write(template.render(**template_args)) 35 | f_out.close() 36 | 37 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/message/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to help with generation and verification of 3 | verilog message stream code. 4 | """ 5 | 6 | from fpga_sdrlib.generate import copyfile 7 | 8 | blocks = { 9 | # The basic modules. 10 | 'message_stream_combiner.v': (('flow/buffer_AA.v',), copyfile, {}), 11 | 'message_slicer.v': (('flow/buffer_BB.v',), copyfile, {}), 12 | 'sample_msg_splitter.v': (None, copyfile, {}), 13 | 'smaller.v': (None, copyfile, {}), 14 | # Some dut's for incompatible icarus simulations. 15 | 'dut_message_stream_combiner.v': (None, copyfile, {}), 16 | 'dut_message_slicer.v': (None, copyfile, {}), 17 | # A qa_wrapper module containing just a sample_msg_splitter. 18 | # Drops the split out messages. 19 | 'qa_sample_msg_splitter.v': (None, copyfile, {}), 20 | # Same but drops the samples and returns the messages. 21 | 'qa_sample_msg_splitter_returns_msgs.v': (None, copyfile, {}), 22 | # A qa_wrapper module combining a sample_msg_splitter 23 | # and a message_stream_combiner. 24 | 'qa_combo.v': (None, copyfile, {}), 25 | # A qa_wrapper module combining a split 26 | # and a message_stream_combiner. 27 | 'qa_splitcombiner.v': (None, copyfile, {}), 28 | # A message_stream_combiner with one empty stream of samples 29 | # and another always empty stream. 30 | 'qa_message_stream_combiner_one.v': (None, copyfile, {}), 31 | # A message_stream_combiner with one empty stream of samples 32 | # and another always empty stream then put through the bits module 33 | # so we can see the individual bits. 34 | 'qa_message_stream_combiner_bits.v': (None, copyfile, {}), 35 | # Test sending back debug messages. 36 | 'qa_debug.v': (('smaller.v', 'message_slicer.v',), copyfile, {}), 37 | } 38 | 39 | # compatible with running on the B100 40 | compatibles = { 41 | 'debug': 42 | ('qa_debug.v', 'uhd/qa_wrapper.v'), 43 | 'sample_msg_splitter': 44 | ('sample_msg_splitter.v', 'qa_sample_msg_splitter.v'), 45 | 'sample_msg_splitter_returns_msgs': 46 | ('sample_msg_splitter.v', 'qa_sample_msg_splitter_returns_msgs.v'), 47 | 'combo': 48 | ('sample_msg_splitter.v', 'message_stream_combiner.v', 'qa_combo.v'), 49 | 'splitcombiner': 50 | ('flow/split.v', 'message_stream_combiner.v', 'qa_splitcombiner.v'), 51 | 'message_stream_combiner_one': 52 | ('message_stream_combiner.v', 'qa_message_stream_combiner_one.v'), 53 | 'message_stream_combiner_bits': 54 | ('message_stream_combiner.v', 'qa_message_stream_combiner_bits.v', 'uhd/bits.v'), 55 | } 56 | 57 | # Not compatible with running on the B100 58 | incompatibles = { 59 | 'message_stream_combiner': 60 | ('message_stream_combiner.v', 'dut_message_stream_combiner.v'), 61 | 'message_slicer': 62 | ('message_slicer.v', 'dut_message_slicer.v'), 63 | } 64 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/message/build.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import cmath 5 | import math 6 | import os 7 | import logging 8 | import time 9 | 10 | from fpga_sdrlib import config, b100 11 | from fpga_sdrlib.buildutils import copyfile, format_template 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | 16 | def logceil(n): 17 | val = int(math.ceil(float(math.log(n))/math.log(2))) 18 | # To keep things simple never return 0. 19 | # Declaring reg with 0 length is not legal. 20 | if val == 0: 21 | val = 1 22 | return val 23 | 24 | def check_builddir(): 25 | messagebuilddir = os.path.join(config.builddir, 'message') 26 | if not os.path.exists(messagebuilddir): 27 | os.makedirs(messagebuilddir) 28 | 29 | def generate_stream_combiner_files(): 30 | """ 31 | Generate the files for the message stream combiner. 32 | """ 33 | check_builddir() 34 | inputfiles = ['message_stream_combiner.v'] 35 | outputfiles = [] 36 | for f in inputfiles: 37 | outputfiles.append(copyfile('message', f)) 38 | return outputfiles 39 | 40 | def generate_slicer_files(): 41 | """ 42 | Generate the files for the message slicer. 43 | """ 44 | check_builddir() 45 | inputfiles = ['message_slicer.v'] 46 | outputfiles = [] 47 | for f in inputfiles: 48 | outputfiles.append(copyfile('message', f)) 49 | return outputfiles 50 | 51 | def generate_sample_msg_splitter_files(): 52 | check_builddir() 53 | return [copyfile('message', 'sample_msg_splitter.v')] 54 | 55 | def generate_files(): 56 | return generate_stream_combiner_files() + generate_slicer_files() 57 | 58 | def generate_sample_msg_splitter_B100_image(name, defines=config.default_defines): 59 | message_builddir= os.path.join(config.builddir, 'message') 60 | inputfiles = generate_sample_msg_splitter_files() 61 | inputfiles.append(copyfile('message', 'qa_sample_msg_splitter.v')) 62 | b100.make_make(name, message_builddir, inputfiles, defines) 63 | for f in inputfiles: 64 | b100.prefix_defines(f, defines) 65 | b100.synthesise(name, message_builddir) 66 | 67 | def generate_combo_B100_image(name, defines=config.default_defines): 68 | message_builddir= os.path.join(config.builddir, 'message') 69 | inputfiles = generate_sample_msg_splitter_files() 70 | inputfiles += generate_stream_combiner_files() 71 | inputfiles.append(copyfile('message', 'qa_combo.v')) 72 | b100.make_make(name, message_builddir, inputfiles, defines) 73 | for f in inputfiles: 74 | b100.prefix_defines(f, defines) 75 | b100.synthesise(name, message_builddir) 76 | 77 | def generate_stream_combiner_executable( 78 | method, n_streams, width, input_buffer_length, max_packet_length): 79 | """ 80 | Generate an executable for the stream combiner DUT. 81 | Use for MyHDL testing. 82 | 83 | Args: 84 | method: What are we generating for (icarus or xilinx). 85 | n_streams: Number of message streams to combine. 86 | width: Bit width of a data block. 87 | input_buffer_length: Number of data blocks in each input buffer. 88 | max_packet_length: Maximum number of data blocks in a packet. 89 | """ 90 | assert(method in ('icarus', 'xilinx')) 91 | check_builddir() 92 | # Check that buffer lengths are power of two 93 | log_input_buffer_length = math.log(input_buffer_length)/math.log(2) 94 | if log_input_buffer_length != int(log_input_buffer_length): 95 | raise ValueError("input_buffer_length must be a power of 2") 96 | dut_msc_fn = copyfile('message', 'dut_message_stream_combiner.v') 97 | executable = "message_stream_combiner" 98 | executable = os.path.join(config.builddir, 'message', executable) 99 | inputfiles = generate_stream_combiner_files() 100 | inputfilestr = ' '.join(inputfiles + [dut_msc_fn]) 101 | cmd = ("iverilog -o {executable} -DN_STREAMS={n_streams} -DLOG_N_STREAMS={log_n_streams} " 102 | "-DWIDTH={width} -DINPUT_BUFFER_LENGTH={input_buffer_length} " 103 | "-DLOG_INPUT_BUFFER_LENGTH={log_input_buffer_length} " 104 | "-DMAX_PACKET_LENGTH={max_packet_length} " 105 | "-DLOG_MAX_PACKET_LENGTH={log_max_packet_length} " 106 | "{inputfiles} " 107 | ).format(executable=executable, n_streams=n_streams, 108 | log_n_streams=logceil(n_streams), 109 | width=width, 110 | input_buffer_length=input_buffer_length, 111 | log_input_buffer_length=int(log_input_buffer_length), 112 | max_packet_length=max_packet_length, 113 | log_max_packet_length=logceil(max_packet_length), 114 | inputfiles=inputfilestr) 115 | logger.debug(cmd) 116 | os.system(cmd) 117 | return executable 118 | 119 | def generate_slicer_executable(method, n_slices, width, buffer_length): 120 | """ 121 | Generate the files for creating message streams. 122 | 123 | Args: 124 | method: What are we generating for (icarus or xilinx). 125 | n_slices: How many times bigger the input is that width. 126 | width: Bit width of a data block. 127 | buffer_length: Number of data blocks in the buffer. 128 | """ 129 | assert(method in ('icarus', 'xilinx')) 130 | check_builddir() 131 | # Check that buffer lengths are power of two 132 | log_buffer_length = math.log(buffer_length)/math.log(2) 133 | if log_buffer_length != int(log_buffer_length): 134 | raise ValueError("buffer_length must be a power of 2") 135 | dut_ms_fn = copyfile('message', 'dut_message_slicer.v') 136 | inputfiles = generate_slicer_files() 137 | executable = "message_slicer" 138 | executable = os.path.join(config.builddir, 'message', executable) 139 | inputfiles = [os.path.join(config.builddir, 'message', f) for f in inputfiles] 140 | inputfilestr = ' '.join(inputfiles + [dut_ms_fn]) 141 | cmd = ("iverilog -o {executable} -DN_SLICES={n_slices} " 142 | "-DLOG_N_SLICES={log_n_slices} " 143 | "-DWIDTH={width} -DBUFFER_LENGTH={buffer_length} " 144 | "-DLOG_BUFFER_LENGTH={log_buffer_length} " 145 | "{inputfiles} " 146 | ).format(executable=executable, n_slices=n_slices, 147 | log_n_slices=int(math.ceil(math.log(n_slices)/math.log(2))), 148 | width=width, 149 | buffer_length=buffer_length, 150 | log_buffer_length=int(log_buffer_length), 151 | inputfiles=inputfilestr) 152 | logger.debug(cmd) 153 | os.system(cmd) 154 | return executable 155 | 156 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/message/msg_codes.py: -------------------------------------------------------------------------------- 1 | from fpga_sdrlib import config 2 | 3 | errorcode_shift = 1 4 | errorcode_mod = pow(2, config.msg_errorcode_width) 5 | modulecode_shift = errorcode_shift * errorcode_mod 6 | modulecode_mod = pow(2, config.msg_modulecode_width) 7 | formatcode_shift = modulecode_shift * modulecode_mod 8 | formatcode_mod = pow(2, config.msg_formatcode_width) 9 | length_shift = formatcode_shift * formatcode_mod 10 | length_mod = pow(2, config.msg_length_width) 11 | header_shift = length_shift * length_mod 12 | 13 | def parse_packet(packet): 14 | header = packet[0] 15 | is_header = header//header_shift 16 | length = (header//length_shift) % length_mod 17 | formatcode = (header//formatcode_shift) % formatcode_mod 18 | modulecode = (header//modulecode_shift) % modulecode_mod 19 | errorcode = (header//errorcode_shift) % errorcode_mod 20 | length = (header//length_shift) % length_mod 21 | if not is_header: 22 | raise StandardError("Header of packet thinks it's not a header.") 23 | if len(packet) != length+1: 24 | raise StandardError("Packet is of length {0} but thinks it is of length {1}.".format(len(packet), length+1)) 25 | if modulecode not in packet_codes: 26 | raise StandardError("The module code {0} is unknown.".format(module_code)) 27 | if errorcode not in packet_codes[modulecode]: 28 | raise StandardError("The error code {0} is unknown for module {1}".format(errorcode, modulecode)) 29 | return packet_codes[modulecode][errorcode](packet) 30 | 31 | def nothing_test_packet(packet): 32 | if len(packet) != 2: 33 | raise StandardError("Packet of type 0, 0 should have length 2") 34 | return "nothing: received {0}".format(packet[1]) 35 | 36 | packet_codes = { 37 | 0: { 38 | 0: nothing_test_packet, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/message/msg_utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from fpga_sdrlib.config import msg_length_width, msg_width, msg_errorcode_width 4 | 5 | def generate_header(length, bits_for_length, width, target=None): 6 | if target is None: 7 | info_max = int(pow(2, width-1-bits_for_length)-1) 8 | info = random.randint(0, info_max) 9 | else: 10 | info = target * pow(2, msg_errorcode_width) 11 | header = (1 << int(width-1)) + (length << int(width-1-bits_for_length)) + info 12 | return header 13 | 14 | def generate_random_packet(length, bits_for_length, width): 15 | packet = [] 16 | packet.append(generate_header(length, bits_for_length, width)) 17 | block_max = int(pow(2, width-1)-1) 18 | for j in range(length): 19 | d = random.randint(0, block_max) 20 | packet.append(d) 21 | return packet 22 | 23 | def packet_from_content(content, bits_for_length=msg_length_width, 24 | width=msg_width, target=0): 25 | l = len(content) 26 | packet = [] 27 | packet.append(generate_header(l, bits_for_length, width, target)) 28 | packet.extend(content) 29 | return packet 30 | 31 | def generate_random_packets(max_length, n_packets, bits_for_length, width, prob_start=1, myrand=random.Random(), none_sample=True): 32 | """ 33 | Generate a data stream containing a bunch of random packets. 34 | The lengths are distributed uniformly up to max_length-1. 35 | 36 | """ 37 | data = [] 38 | packets = [] 39 | sample_max = int(pow(2, width-2)) 40 | assert(pow(2, bits_for_length) >= max_length) 41 | for i in range(n_packets): 42 | while (myrand.random() > prob_start): 43 | if none_sample: 44 | data.append(None) 45 | else: 46 | data.append(myrand.randint(0, sample_max)) 47 | l = myrand.randint(0, max_length) 48 | packet = generate_random_packet(l, bits_for_length, width) 49 | data.extend(packet) 50 | packets.append(packet) 51 | return data, packets 52 | 53 | def stream_to_packets(stream, bits_for_length=msg_length_width, width=msg_width, allow_samples=True): 54 | header_shift = pow(2, width-1) 55 | length_shift1 = pow(2, width-1-bits_for_length) 56 | length_shift2 = pow(2, bits_for_length) 57 | in_packet = False 58 | packet_length = None 59 | packet_pos = None 60 | packet = None 61 | packets = [] 62 | for block in stream: 63 | if block is None: 64 | continue 65 | header = block // header_shift 66 | if header: 67 | packet_length = block // length_shift1 - header*length_shift2 68 | if in_packet: 69 | raise ValueError("Got a header when we weren't expecting it. Pos is {0}. New length is {1}".format(packet_pos, packet_length)) 70 | if packet_length > 0: 71 | packet_pos = 0 72 | in_packet = True 73 | packet = [block] 74 | else: 75 | packets.append([block]) 76 | if (not header) and (not in_packet): 77 | # Treat a sample as a packet of length 1. 78 | if not allow_samples: 79 | raise ValueError("No header found when expecting one.") 80 | packets.append([block]) 81 | if (not header) and in_packet: 82 | packet.append(block) 83 | packet_pos += 1 84 | if packet_pos == packet_length: 85 | packets.append(packet) 86 | packet = None 87 | in_packet = False 88 | packet_pos = None 89 | if packet is not None: 90 | print(packet) 91 | raise ValueError("Incomplete packets") 92 | return packets 93 | 94 | def stream_to_samples_and_packets(stream, bits_for_length=msg_length_width, width=msg_width): 95 | header_shift = pow(2, width-1) 96 | mixed = stream_to_packets(stream, bits_for_length, width, True) 97 | packets = [] 98 | samples = [] 99 | for p in mixed: 100 | if len(p) > 1: 101 | packets.append(p) 102 | else: 103 | header = p[0] // header_shift 104 | if header: 105 | packets.append(p) 106 | else: 107 | samples.append(p[0]) 108 | return samples, packets 109 | 110 | 111 | def make_packet_dict(packets): 112 | packet_dict = {} 113 | for p in packets: 114 | if p[0] in packet_dict: 115 | raise StandardError("Unlikely clash between packets. Redo with another random seed.") 116 | packet_dict[p[0]] = p 117 | return packet_dict 118 | 119 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/uhd/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generates some wrapper QA files for use with testing on a USRP. 3 | """ 4 | 5 | from fpga_sdrlib.generate import copyfile 6 | 7 | blocks = { 8 | 'dut_qa_wrapper.v': (None, copyfile, {}), 9 | 'dut_qa_contents.v': (None, copyfile, {}), 10 | 'qa_wrapper_null.v': (None, copyfile, {}), 11 | 'bits.v': (None, copyfile, {}), 12 | 'qa_wrapper_bits.v': (('bits.v',), copyfile, {}), 13 | 'qa_wrapper.v': (('message/sample_msg_splitter.v', 14 | 'message/message_stream_combiner.v',), 15 | copyfile, {}), 16 | } 17 | 18 | compatibles = { 19 | 'null': ('qa_wrapper_null.v',), 20 | 'bits': ('qa_wrapper_bits.v',), 21 | } 22 | incompatibles = {} 23 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/uhd/build.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Ben Reynwar 2 | # Released under MIT License (see LICENSE.txt) 3 | 4 | import os 5 | 6 | from fpga_sdrlib import config 7 | from fpga_sdrlib.buildutils import copyfile, format_template, make_define_string 8 | from fpga_sdrlib.message.build import generate_sample_msg_splitter_files, generate_stream_combiner_files 9 | 10 | def generate_qa_wrapper_files(): 11 | """ 12 | Generate the files to make the 'nothing' block. 13 | """ 14 | uhd_builddir= os.path.join(config.builddir, 'uhd') 15 | if not os.path.exists(uhd_builddir): 16 | os.makedirs(uhd_builddir) 17 | inputfiles = [copyfile('uhd', 'qa_wrapper.v')] 18 | inputfiles += generate_sample_msg_splitter_files() 19 | inputfiles += generate_stream_combiner_files() 20 | return inputfiles 21 | 22 | def generate_qa_wrapper_null_files(): 23 | uhd_builddir= os.path.join(config.builddir, 'uhd') 24 | if not os.path.exists(uhd_builddir): 25 | os.makedirs(uhd_builddir) 26 | inputfiles = [copyfile('uhd', 'qa_wrapper_null.v')] 27 | return inputfiles 28 | 29 | -------------------------------------------------------------------------------- /python/fpga_sdrlib/uhd/qa_uhd.py: -------------------------------------------------------------------------------- 1 | import random 2 | import logging 3 | import unittest 4 | 5 | from fpga_sdrlib import config, buildutils 6 | from fpga_sdrlib.testbench import TestBenchIcarusOuter, TestBenchB100 7 | from fpga_sdrlib.generate import logceil 8 | 9 | def bits_to_int(bits): 10 | f = 1 11 | s = 0 12 | for b in reversed(bits): 13 | s += b*f 14 | f *= 2 15 | return s 16 | 17 | class TestNull(unittest.TestCase): 18 | 19 | def setUp(self): 20 | self.rg = random.Random(0) 21 | 22 | def atest_null(self): 23 | """ 24 | Tests the null qa_wrapper. 25 | """ 26 | width=32 27 | max_sample = pow(2, width-2)-1 28 | n_samples = 10 29 | data = [self.rg.randint(0, max_sample) for i in range(n_samples)] 30 | executable = buildutils.generate_icarus_executable( 31 | 'uhd', 'null', '-test') 32 | fpgaimage = buildutils.generate_B100_image( 33 | 'uhd', 'null', '-test') 34 | tb_icarus = TestBenchIcarusOuter(executable, in_raw=data) 35 | tb_b100 = TestBenchB100(fpgaimage, in_raw=data) 36 | for tb, steps in ( 37 | (tb_icarus, 5000), 38 | (tb_b100, 10000), 39 | ): 40 | tb.run(steps) 41 | self.assertEqual(len(data), len(tb.out_raw)) 42 | for e, r in zip(data, tb.out_raw): 43 | self.assertEqual(e, r) 44 | 45 | def test_bits(self): 46 | """ 47 | Tests the bits qa_wrapper. 48 | """ 49 | width=32 50 | sendnth = 70 51 | max_sample = pow(2, width)-1 52 | n_samples = 2 53 | data = [self.rg.randint(1, max_sample) for i in range(n_samples)] 54 | defines = config.updated_defines( 55 | { 56 | 'LOG_WIDTH': logceil(width), 57 | 'ERRORCODE': 666, 58 | 'WIDTH': width, 59 | 'LOG_SENDNTH': 12, 60 | }) 61 | executable = buildutils.generate_icarus_executable( 62 | 'uhd', 'bits', '-test', defines=defines) 63 | fpgaimage = buildutils.generate_B100_image( 64 | 'uhd', 'bits', '-test', defines=defines) 65 | tb_icarus = TestBenchIcarusOuter(executable, in_raw=data, sendnth=sendnth) 66 | tb_b100 = TestBenchB100(fpgaimage, in_raw=data) 67 | for tb, steps in ( 68 | (tb_icarus, 5000), 69 | (tb_b100, 100000), 70 | ): 71 | tb.run(steps) 72 | start_pos = None 73 | for i, x in enumerate(tb.out_raw): 74 | if (x==width-1): 75 | start_pos = i 76 | break 77 | for i, x in reversed(zip(range(0, len(tb.out_raw)), tb.out_raw)): 78 | if (x==width-1): 79 | stop_pos = i 80 | break 81 | if start_pos is None: 82 | raise ValueError("{0} not found in output".format(width-1)) 83 | out = tb.out_raw[start_pos: stop_pos + 2*width] 84 | bitted = [out[i*2*width+1:(i+1)*2*width+1:2] for i in range(len(out)/width/2)] 85 | poses = [out[i*2*width:(i+1)*2*width:2] for i in range(len(out)/width/2)] 86 | expected = [31-x for x in range(32)] 87 | for i, p in enumerate(poses): 88 | if (p != expected): 89 | print(i) 90 | print(p) 91 | self.assertEqual(p, expected) 92 | r_ints = [bits_to_int(bits) for bits in bitted] 93 | r_ints = [x for x in r_ints if x != 0] 94 | self.assertEqual(len(data), len(r_ints)) 95 | for e, r in zip(data, r_ints): 96 | self.assertEqual(e, r) 97 | 98 | if __name__ == '__main__': 99 | config.setup_logging(logging.DEBUG) 100 | unittest.main() 101 | -------------------------------------------------------------------------------- /verilog/channelizer/channelizer.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module channelizer 6 | #( 7 | parameter N = 8, 8 | parameter LOGN = 3, 9 | parameter WDTH = 32, 10 | parameter MWDTH = 1, 11 | parameter FLTLEN = 10, 12 | parameter LOG_FLTLEN = 4 13 | ) 14 | ( 15 | input wire clk, 16 | input wire rst_n, 17 | input wire [WDTH-1:0] in_data, 18 | input wire in_nd, 19 | input wire [MWDTH-1:0] in_m, 20 | input wire [`MSG_WIDTH-1:0] in_msg, 21 | input wire in_msg_nd, 22 | output reg [WDTH-1:0] out_data, 23 | output reg out_nd, 24 | output reg [MWDTH-1:0] out_m, 25 | output reg [`MSG_WIDTH-1:0] out_msg, 26 | output reg out_msg_nd, 27 | output wire error, 28 | output reg first_channel 29 | ); 30 | 31 | wire filtered_nd; 32 | wire [WDTH-1:0] filtered_data; 33 | wire [MWDTH-1:0] filtered_m; 34 | wire filtered_error; 35 | wire filtered_ff; 36 | reg unaligned; 37 | wire channelized_error; 38 | 39 | assign error = (filtered_error | channelized_error | unaligned); 40 | 41 | always @ (posedge clk) 42 | begin 43 | if (rst_n) 44 | begin 45 | // $display("error is %d", error); 46 | if (filtered_error) 47 | $display("FILTERED_ERROR"); 48 | if (channelized_error) 49 | $display("CHANNELIZED_ERROR"); 50 | if (unaligned) 51 | $display("UNALIGNED"); 52 | end 53 | end 54 | 55 | filterbank_ccf #(N, LOGN, WDTH, MWDTH, FLTLEN, LOG_FLTLEN) filterbank_ccf_i 56 | (.clk(clk), 57 | .rst_n(rst_n), 58 | .in_data(in_data), 59 | .in_nd(in_nd), 60 | .in_m(in_m), 61 | .in_msg(in_msg), 62 | .in_msg_nd(in_msg_nd), 63 | .out_data(filtered_data), 64 | .out_nd(filtered_nd), 65 | .out_m(filtered_m), 66 | .first_filter(filtered_ff), 67 | .error(filtered_error) 68 | ); 69 | 70 | wire [WDTH-1:0] channelized_data; 71 | wire channelized_nd; 72 | wire [MWDTH-1:0] channelized_m; 73 | wire channelized_ff; 74 | wire channelized_first; 75 | 76 | dit #(N, LOGN, WDTH/2, WDTH/2, MWDTH+1) dit_i 77 | (.clk(clk), 78 | .rst_n(rst_n), 79 | .in_x(filtered_data), 80 | .in_nd(filtered_nd), 81 | .in_w({filtered_m, filtered_ff}), 82 | .out_x(channelized_data), 83 | .out_nd(channelized_nd), 84 | .out_w({channelized_m, channelized_ff}), 85 | .first(channelized_first), 86 | .overflow(channelized_error) 87 | ); 88 | 89 | // Keep the channels we want. 90 | reg [N-1:0] default_desired_channels; 91 | reg [N-1:0] desired_channels; 92 | reg [LOGN-1:0] channel; 93 | reg looking_for_first_channel; 94 | initial 95 | begin 96 | channel <= {LOGN{1'b0}}; 97 | default_desired_channels <= {N{1'b1}}; 98 | desired_channels <= {N{1'b1}}; 99 | unaligned <= 1'b0; 100 | looking_for_first_channel <= 1'b1; 101 | end 102 | always @ (posedge clk) 103 | begin 104 | if (~rst_n) 105 | begin 106 | channel <= {LOGN{1'b0}}; 107 | desired_channels <= default_desired_channels; 108 | unaligned <= 1'b0; 109 | looking_for_first_channel <= 1'b1; 110 | end 111 | else 112 | begin 113 | if (channelized_nd) 114 | begin 115 | // $display("channelized_ff %d channelized_first %d", channelized_ff, channelized_first); 116 | if (channelized_ff != channelized_first) 117 | unaligned <= 1'b1; 118 | if (channelized_first) 119 | if (|channel) 120 | unaligned <= 1'b1; 121 | if (desired_channels[channel]) 122 | begin 123 | if (looking_for_first_channel) 124 | begin 125 | looking_for_first_channel <= 1'b0; 126 | first_channel <= 1'b1; 127 | end 128 | else 129 | first_channel <= 1'b0; 130 | out_data <= channelized_data; 131 | out_nd <= 1'b1; 132 | out_m <= channelized_m; 133 | end 134 | else 135 | out_nd <= 1'b0; 136 | if (&channel) 137 | looking_for_first_channel <= 1'b1; 138 | channel <= channel + 1; 139 | end 140 | else 141 | out_nd <= 1'b0; 142 | end 143 | end 144 | 145 | endmodule -------------------------------------------------------------------------------- /verilog/channelizer/dut_channelizer.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is a wrapper around the channelizer module so that it can be 5 | // accessed from the myhdl test bench. 6 | 7 | module dut_channelizer; 8 | reg clk; 9 | reg rst_n; 10 | reg [`WIDTH-1:0] in_data; 11 | reg in_nd; 12 | reg [`MWIDTH-1:0] in_m; 13 | reg [`MSG_WIDTH-1:0] in_msg; 14 | reg in_msg_nd; 15 | wire [`WIDTH-1:0] out_data; 16 | wire out_nd; 17 | wire [`MWIDTH-1:0] out_m; 18 | wire [`MSG_WIDTH-1:0] out_msg; 19 | wire out_msg_nd; 20 | wire error; 21 | wire first_channel; 22 | 23 | initial begin 24 | $from_myhdl(clk, rst_n, in_data, in_nd, in_m, in_msg, in_msg_nd); 25 | $to_myhdl(out_data, out_nd, out_m, out_msg, out_msg_nd, error, first_channel); 26 | end 27 | 28 | channelizer #(`N, `LOG_N, `WIDTH, `MWIDTH, `FLTLEN, `LOG_FLTLEN) dut 29 | (clk, rst_n, 30 | in_data, in_nd, in_m, in_msg, in_msg_nd, 31 | out_data, out_nd, out_m, out_msg, out_msg_nd, 32 | error, first_channel); 33 | 34 | endmodule -------------------------------------------------------------------------------- /verilog/channelizer/qa_channelizer.v.t: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is a wrapper around the channelizer module so that it can be 5 | // accessed from the myhdl test bench. 6 | 7 | module qa 8 | ( 9 | input wire clk, 10 | input wire reset, 11 | output wire [{{width}}-1:0] out_data, 12 | output wire out_nd 13 | ); 14 | 15 | wire out_error_data_source; 16 | wire out_error_channelizer; 17 | wire out_first_channel; 18 | wire [{{mwidth}}-1:0] in_m; 19 | wire [{{mwidth}}-1:0] out_m; 20 | wire rst_n; 21 | 22 | assign rst_n = ~reset; 23 | 24 | wire [{{width}}-1:0] in_data; 25 | wire in_nd; 26 | 27 | data_source 28 | #({{sendnth}}, {{logsendnth}}, {{width}}, 29 | {{mwidth}}, {{n_data}}, {{logndata}}) 30 | data_source_i 31 | (clk, rst_n, in_nd, in_data, in_m, out_error_data_source); 32 | 33 | channelizer 34 | #({{n}}, {{logn}}, {{width}},{{mwidth}}, {{fltlen}}) 35 | dut 36 | (clk, rst_n, in_data, in_nd, in_m, out_data, 37 | out_nd, out_m, out_error, out_first_channel); 38 | 39 | endmodule -------------------------------------------------------------------------------- /verilog/fft/buffer_BB_to_stage.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // Connects a buffer_BB to a stage and an mstore and takes care of 6 | // transfering data. 7 | 8 | module buffer_BB_to_stage 9 | #( 10 | parameter N = 8, 11 | parameter LOG_N = 3, 12 | parameter WIDTH = 32, 13 | parameter MWIDTH = 1 14 | ) 15 | ( 16 | input wire clk, 17 | input wire rst_n, 18 | // Start signals 19 | input wire start, 20 | // From buffer_BB 21 | input wire read_full, 22 | input wire [WIDTH+MWIDTH-1: 0] read_data, 23 | output reg read_delete, 24 | // To Stage 25 | output wire [LOG_N-1:0] out_addr0, 26 | output wire [LOG_N-1:0] out_addr1, 27 | output reg out_nd, 28 | output reg [WIDTH-1:0] out_data0, 29 | output reg [WIDTH-1:0] out_data1, 30 | // To mStore 31 | output reg out_mnd, 32 | output reg [MWIDTH-1:0] out_m, 33 | // Whether it is active 34 | output wire active, 35 | output reg error 36 | ); 37 | 38 | reg [LOG_N-1:0] addr; 39 | assign out_addr0 = addr; 40 | assign out_addr1 = addr + 1; 41 | reg read_counter; 42 | wire [WIDTH-1:0] read_data_s; 43 | wire [MWIDTH-1:0] read_data_m; 44 | assign {read_data_s, read_data_m} = read_data; 45 | reg first_read; 46 | reg active_o; 47 | 48 | assign active = active_o | start; 49 | 50 | always @ (posedge clk) 51 | begin 52 | // Set the default values; 53 | out_nd <= 1'b0; 54 | read_delete <= 1'b0; 55 | out_mnd <= 1'b0; 56 | if (~rst_n) 57 | begin 58 | active_o <= 1'b0; 59 | addr <= {LOG_N{1'b0}}; 60 | read_counter <= 1'b0; 61 | error <= 1'b0; 62 | end 63 | else if (start) 64 | begin 65 | if (active_o) 66 | error <= 1'b1; 67 | else 68 | begin 69 | active_o <= 1'b1; 70 | addr <= {LOG_N{1'b0}}; 71 | read_counter <= 1'b0; 72 | first_read <= 1'b1; 73 | end 74 | end 75 | else if (active_o & read_full) 76 | begin 77 | out_mnd <= 1'b1; 78 | out_m <= read_data_m; 79 | // We can only read one item from the buffer each block 80 | // cycle. But we write to the stage two at a time 81 | // so we have to save values and only write every second 82 | // clock cycle. 83 | read_counter <= ~read_counter; 84 | read_delete <= 1'b1; 85 | if (~read_counter) 86 | begin 87 | out_data0 <= read_data_s; 88 | if (~first_read) 89 | addr <= addr + 2; 90 | first_read <= 1'b0; 91 | end 92 | else 93 | begin 94 | out_data1 <= read_data_s; 95 | out_nd <= 1'b1; 96 | if (addr == N-2) 97 | begin 98 | active_o <= 1'b0; 99 | end 100 | end 101 | end 102 | end 103 | endmodule -------------------------------------------------------------------------------- /verilog/fft/butterfly.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | /* 6 | Implements a butterfly module for a FFT. 7 | 8 | Takes complex numbers W, XA, XB and returns 9 | YA = XA + W*XB 10 | YB = XA - W*XB 11 | 12 | It can take input no more frequently than once every 13 | two steps. This is so, hopefully, less multiply 14 | blocks can be used. 15 | */ 16 | 17 | module butterfly 18 | #( 19 | parameter WIDTH = 32, 20 | parameter MWIDTH = 1 21 | ) 22 | ( 23 | input wire clk, 24 | input wire rst_n, 25 | // m_in contains data that passes through this block with no change. 26 | input wire [MWIDTH-1:0] m_in, 27 | // The twiddle factor. 28 | input wire signed [WIDTH-1:0] w, 29 | // XA 30 | input wire signed [WIDTH-1:0] xa, 31 | // XB 32 | input wire signed [WIDTH-1:0] xb, 33 | // Set to 1 when new data is present on inputs. 34 | input wire x_nd, 35 | // delayed version of m_in. 36 | output reg [MWIDTH-1:0] m_out, 37 | // YA = XA + W*XB 38 | // YB = XA - W*XB 39 | output wire signed [WIDTH-1:0] ya, 40 | output wire signed [WIDTH-1:0] yb, 41 | output reg y_nd, 42 | `ifdef DEBUG 43 | output wire [WIDTH-1:0] out_msg, 44 | output wire out_msg_nd, 45 | output wire error 46 | `else 47 | output reg error 48 | `endif 49 | ); 50 | 51 | // Set wire to the real and imag parts for convenience. 52 | wire signed [WIDTH/2-1:0] xa_re; 53 | wire signed [WIDTH/2-1:0] xa_im; 54 | assign xa_re = xa[WIDTH-1:WIDTH/2]; 55 | assign xa_im = xa[WIDTH/2-1:0]; 56 | wire signed [WIDTH/2-1: 0] ya_re; 57 | wire signed [WIDTH/2-1: 0] ya_im; 58 | assign ya = {ya_re, ya_im}; 59 | wire signed [WIDTH/2-1: 0] yb_re; 60 | wire signed [WIDTH/2-1: 0] yb_im; 61 | assign yb = {yb_re, yb_im}; 62 | 63 | // Delayed stuff. 64 | reg signed [WIDTH/2-1:0] xa_re_z; 65 | reg signed [WIDTH/2-1:0] xa_im_z; 66 | // Output of multiplier 67 | wire signed [WIDTH-1:0] xbw; 68 | wire signed [WIDTH/2-1:0] xbw_re; 69 | wire signed [WIDTH/2-1:0] xbw_im; 70 | assign xbw_re = xbw[WIDTH-1:WIDTH/2]; 71 | assign xbw_im = xbw[WIDTH/2-1:0]; 72 | // Do summing 73 | // I don't think we should get overflow here because of the 74 | // size of the twiddle factors. 75 | // If we do testing should catch it. 76 | assign ya_re = xa_re_z + xbw_re; 77 | assign ya_im = xa_im_z + xbw_im; 78 | assign yb_re = xa_re_z - xbw_re; 79 | assign yb_im = xa_im_z - xbw_im; 80 | 81 | // Create the multiply module. 82 | multiply_complex #(WIDTH) multiply_complex_0 83 | (.clk(clk), 84 | .rst_n(rst_n), 85 | .x(xb), 86 | .y(w), 87 | .z(xbw) 88 | ); 89 | 90 | `ifdef DEBUG 91 | // Process debug messages. 92 | reg [5*WIDTH-1:0] msg; 93 | reg msg_nd; 94 | wire [WIDTH-1:0] xa_s; 95 | wire [WIDTH-1:0] xbw_s; 96 | wire [WIDTH-1:0] ya_s; 97 | wire [WIDTH-1:0] yb_s; 98 | smaller #(WIDTH) smaller_0 ({xa_re_z, xa_im_z}, xa_s); 99 | smaller #(WIDTH) smaller_1 (xbw, xbw_s); 100 | smaller #(WIDTH) smaller_2 (ya, ya_s); 101 | smaller #(WIDTH) smaller_3 (yb, yb_s); 102 | message_slicer #(5, WIDTH, 48) message_slicer_0 103 | (clk, rst_n, msg, msg_nd, out_msg, out_msg_nd, error); 104 | `endif 105 | 106 | always @ (posedge clk) 107 | begin 108 | if (!rst_n) 109 | begin 110 | y_nd <= 1'b0; 111 | `ifndef DEBUG 112 | error <= 1'b0; 113 | `endif 114 | end 115 | else 116 | begin 117 | // Set delay for x_nd_old and m. 118 | `ifdef DEBUG 119 | msg_nd <= 1'b0; 120 | `endif 121 | y_nd <= x_nd; 122 | m_out <= m_in; 123 | if (x_nd) 124 | begin 125 | xa_re_z <= xa_re/2; 126 | xa_im_z <= xa_im/2; 127 | end 128 | `ifdef DEBUG 129 | // We test that xa_s is not equal to zero, so that we don't return debug 130 | // info on the continually streamed 0's. 131 | if (y_nd & (xa_s != 0)) 132 | begin 133 | msg <= {1'b1, 10'd4, 21'd0, xa_s, xbw_s, ya_s, yb_s}; 134 | msg_nd <= 1'b1; 135 | end 136 | `endif 137 | end 138 | end 139 | 140 | endmodule -------------------------------------------------------------------------------- /verilog/fft/dut_dit.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is simply a wrapper around the dit module so that it can be accessed from the 5 | // myhdl test bench. 6 | 7 | module dut_dit; 8 | reg clk; 9 | reg rst_n; 10 | reg [`WIDTH-1:0] in_data; 11 | wire [`WIDTH-1:0] out_data; 12 | reg in_nd; 13 | wire out_nd; 14 | reg [`MWIDTH-1:0] in_m; 15 | wire [`MWIDTH-1:0] out_m; 16 | wire first; 17 | wire error; 18 | 19 | initial begin 20 | $from_myhdl(clk, rst_n, in_data, in_nd, in_m); 21 | $to_myhdl(out_data, out_nd, out_m, first, error); 22 | end 23 | 24 | dit #(`N, `LOG_N, `WIDTH/2, `WIDTH/2, `MWIDTH) dut 25 | (clk, rst_n, in_data, in_nd, in_m, out_data, out_nd, out_m, first, error); 26 | 27 | endmodule -------------------------------------------------------------------------------- /verilog/fft/mstore.v: -------------------------------------------------------------------------------- 1 | module mstore 2 | #( 3 | parameter N = 0, 4 | parameter MWIDTH = 1 5 | ) 6 | ( 7 | input wire clk, 8 | input wire rst_n, 9 | input wire in_nd, 10 | input wire [MWIDTH-1:0] in_m, 11 | input wire in_read, 12 | output wire [MWIDTH-1:0] out_m, 13 | output reg error 14 | ); 15 | 16 | function integer clog2; 17 | input integer value; 18 | begin 19 | value = value-1; 20 | for (clog2=0; value>0; clog2=clog2+1) 21 | value = value>>1; 22 | end 23 | endfunction 24 | 25 | localparam integer LOG_N = clog2(N); 26 | 27 | reg [LOG_N-1:0] addr; 28 | wire [LOG_N-1:0] next_addr; 29 | reg [MWIDTH-1:0] RAM[N-1:0]; 30 | reg filling; 31 | 32 | assign next_addr = addr+1; 33 | assign out_m = (in_read)?RAM[next_addr]:RAM[addr]; 34 | 35 | always @ (posedge clk) 36 | if (~rst_n) 37 | begin 38 | addr <= {LOG_N{1'b0}}; 39 | error <= 1'b0; 40 | filling <= 1'b1; 41 | end 42 | else 43 | begin 44 | // If we have new data make sure we're in the filling 45 | // state and write the data. 46 | if (in_nd) 47 | if (~filling) 48 | error <= 1'b1; 49 | else 50 | RAM[addr] <= in_m; 51 | // If a value has been read make sure we're not in the 52 | // filling state. 53 | else if (in_read) 54 | if (filling) 55 | error <= 1'b1; 56 | // If we have new data or data has been read then we 57 | // increment the position. 58 | if (in_nd | in_read) 59 | if (addr == N-1) 60 | begin 61 | addr <= {LOG_N{1'b0}}; 62 | filling <= ~filling; 63 | end 64 | else 65 | addr <= addr + 1; 66 | end 67 | 68 | endmodule 69 | -------------------------------------------------------------------------------- /verilog/fft/qa_butterfly.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module qa_contents 6 | #( 7 | parameter WIDTH = 32, 8 | parameter MWIDTH = 1 9 | ) 10 | ( 11 | input wire clk, 12 | input wire rst_n, 13 | input wire [WIDTH-1:0] in_data, 14 | input wire in_nd, 15 | input wire [MWIDTH-1:0] in_m, 16 | input wire [`MSG_WIDTH-1:0] in_msg, 17 | input wire in_msg_nd, 18 | output reg [WIDTH-1:0] out_data, 19 | output reg out_nd, 20 | output reg [MWIDTH-1:0] out_m, 21 | output wire [`MSG_WIDTH-1:0] out_msg, 22 | output wire out_msg_nd, 23 | output wire error 24 | ); 25 | 26 | reg [WIDTH-1:0] xa; 27 | reg [WIDTH-1:0] xb; 28 | reg [WIDTH-1:0] w; 29 | reg [2:0] counter; 30 | reg active; 31 | reg x_nd; 32 | reg [MWIDTH-1:0] bf_in_m; 33 | wire [MWIDTH-1:0] bf_out_m; 34 | 35 | always @ (posedge clk) 36 | begin 37 | // Default x_nd 38 | x_nd <= 1'b0; 39 | if (~rst_n) 40 | begin 41 | active <= 1'b0; 42 | counter <= 2'b0; 43 | end 44 | else if (in_nd) 45 | begin 46 | if (((~active)& (in_data != {WIDTH{1'b0}})) | (counter == 2'd0)) 47 | begin 48 | active <= 1'b1; 49 | xa <= in_data; 50 | counter <= 2'd1; 51 | bf_in_m <= in_m; 52 | end 53 | else if (counter == 2'd1) 54 | begin 55 | xb <= in_data; 56 | counter <= 2'd2; 57 | end 58 | else if (counter == 2'd2) 59 | begin 60 | w <= in_data; 61 | counter <= 2'd0; 62 | x_nd <= 1'b1; 63 | end 64 | end 65 | end 66 | 67 | wire [WIDTH-1:0] ya; 68 | wire [WIDTH-1:0] yb; 69 | reg [WIDTH-1:0] yb_old; 70 | wire y_nd; 71 | reg y_nd_old; 72 | reg error_control; 73 | wire error_bf; 74 | assign error = error_control | error_bf; 75 | 76 | always @ (posedge clk) 77 | begin 78 | // default out_nd 79 | out_nd <= 1'b0; 80 | y_nd_old <= y_nd; 81 | if (~rst_n) 82 | begin 83 | error_control <= 1'b0; 84 | y_nd_old <= 1'b0; 85 | end 86 | else if (y_nd) 87 | begin 88 | if (y_nd_old) 89 | error_control <= 1'b1; 90 | yb_old <= yb; 91 | out_data <= ya; 92 | out_nd <= 1'b1; 93 | out_m <= bf_out_m; 94 | end 95 | else if (y_nd_old) 96 | begin 97 | out_data <= yb_old; 98 | out_nd <= 1'b1; 99 | out_m <= {MWIDTH{1'b0}}; 100 | end 101 | end 102 | butterfly 103 | #(.MWIDTH (MWIDTH), 104 | .WIDTH (WIDTH) 105 | ) 106 | butterfly_0 107 | (.clk (clk), 108 | .rst_n (rst_n), 109 | .m_in (bf_in_m), 110 | .w (w), 111 | .xa (xa), 112 | .xb (xb), 113 | .x_nd (x_nd), 114 | .m_out (bf_out_m), 115 | .ya (ya), 116 | .yb (yb), 117 | .y_nd (y_nd), 118 | `ifdef DEBUG 119 | .out_msg(out_msg), 120 | .out_msg_nd(out_msg_nd), 121 | `endif 122 | .error(error_bf) 123 | ); 124 | 125 | 126 | endmodule -------------------------------------------------------------------------------- /verilog/fft/qa_dit.v.t: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // This is a wrapper around the nothing module so that it can be 6 | // incorporated into an FPGA. 7 | 8 | module qa 9 | ( 10 | input wire clk, 11 | input wire reset, 12 | input wire [{{width}}-1:0] in_data, 13 | input wire in_nd, 14 | output wire [{{width}}-1:0] out_data, 15 | output wire out_nd 16 | ); 17 | 18 | wire first; 19 | wire error; 20 | wire [{{mwidth}}-1:0] in_m; 21 | wire [{{mwidth}}-1:0] out_m; 22 | wire rst_n; 23 | 24 | assign rst_n = ~reset; 25 | 26 | dit 27 | #({{n}},{{logn}},{{width}},{{width}},{{mwidth}}) 28 | dut 29 | (clk, rst_n, 30 | in_data, in_nd, in_m, 31 | out_data, out_nd, out_m, 32 | first, error); 33 | 34 | endmodule -------------------------------------------------------------------------------- /verilog/fft/qa_dit_series.v.t: -------------------------------------------------------------------------------- 1 | module qa_contents 2 | #( 3 | parameter WIDTH = 32, 4 | parameter MWIDTH = 1 5 | ) 6 | ( 7 | input wire clk, 8 | input wire rst_n, 9 | input wire [WIDTH-1:0] in_data, 10 | input wire in_nd, 11 | input wire [MWIDTH-1:0] in_m, 12 | // Takes input messages to set taps. 13 | input wire [`MSG_WIDTH-1:0] in_msg, 14 | input wire in_msg_nd, 15 | output wire [WIDTH-1:0] out_data, 16 | output wire out_nd, 17 | output wire [MWIDTH-1:0] out_m, 18 | output wire [`MSG_WIDTH-1:0] out_msg, 19 | output wire out_msg_nd, 20 | output wire error 21 | ); 22 | 23 | dit_series_{{N}} #(WIDTH, MWIDTH, `N) dit_series_0 24 | ( 25 | .clk(clk), 26 | .rst_n(rst_n), 27 | .in_data(in_data), 28 | .in_nd(in_nd), 29 | .in_m(in_m), 30 | .in_msg(in_msg), 31 | .in_msg_nd(in_msg_nd), 32 | .out_data(out_data), 33 | .out_nd(out_nd), 34 | .out_m(out_m), 35 | .out_msg(out_msg), 36 | .out_msg_nd(out_msg_nd), 37 | .error(error) 38 | ); 39 | 40 | endmodule 41 | -------------------------------------------------------------------------------- /verilog/fft/qa_stage.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A single FFT stage. 6 | // We fill it with values and then empty it. 7 | // This is for QA purposes. 8 | 9 | module qa_contents 10 | #( 11 | parameter WIDTH = 32, 12 | parameter MWIDTH = 1 13 | ) 14 | ( 15 | input wire clk, 16 | input wire rst_n, 17 | input wire [WIDTH-1:0] in_data, 18 | input wire in_nd, 19 | input wire [MWIDTH-1:0] in_m, 20 | input wire [`MSG_WIDTH-1:0] in_msg, 21 | input wire in_msg_nd, 22 | output wire [WIDTH-1:0] out_data, 23 | output wire out_nd, 24 | output wire [MWIDTH-1:0] out_m, 25 | output wire [`MSG_WIDTH-1:0] out_msg, 26 | output wire out_msg_nd, 27 | output wire error 28 | ); 29 | 30 | function integer clog2; 31 | input integer value; 32 | begin 33 | value = value-1; 34 | for (clog2=0; value>0; clog2=clog2+1) 35 | value = value>>1; 36 | end 37 | endfunction 38 | 39 | localparam integer LOG_N = clog2(`N); 40 | 41 | localparam integer STAGE_EMPTY = 2'd0; 42 | localparam integer STAGE_WRITING = 2'd1; 43 | localparam integer STAGE_FULL = 2'd2; 44 | localparam integer STAGE_READING = 2'd3; 45 | 46 | wire buffer_read_delete; 47 | wire buffer_read_full; 48 | wire [WIDTH+MWIDTH-1:0] buffer_data; 49 | wire buffer_error; 50 | 51 | buffer_BB #(WIDTH+MWIDTH, `N) buffer_in 52 | ( 53 | .clk(clk), 54 | .rst_n(rst_n), 55 | .write_strobe(in_nd), 56 | .write_data({in_data, in_m}), 57 | .read_delete(buffer_read_delete), 58 | .read_full(buffer_read_full), 59 | .read_data(buffer_data), 60 | .error(buffer_error) 61 | ); 62 | 63 | reg b2s_start; 64 | wire b2s_active; 65 | wire [LOG_N-1:0] b2s_addr0; 66 | wire [LOG_N-1:0] b2s_addr1; 67 | wire b2s_nd; 68 | wire [WIDTH-1:0] b2s_data0; 69 | wire [WIDTH-1:0] b2s_data1; 70 | wire b2s_mnd; 71 | wire [MWIDTH-1:0] b2s_m; 72 | wire b2s_error; 73 | 74 | buffer_BB_to_stage #(`N, LOG_N, WIDTH, MWIDTH) b2s 75 | ( 76 | .clk(clk), 77 | .rst_n(rst_n), 78 | .start(b2s_start), 79 | .read_full(buffer_read_full), 80 | .read_data(buffer_data), 81 | .read_delete(buffer_read_delete), 82 | .out_addr0(b2s_addr0), 83 | .out_addr1(b2s_addr1), 84 | .out_nd(b2s_nd), 85 | .out_data0(b2s_data0), 86 | .out_data1(b2s_data1), 87 | .out_mnd(b2s_mnd), 88 | .out_m(b2s_m), 89 | .active(b2s_active), 90 | .error(b2s_error) 91 | ); 92 | 93 | wire mbuffer_read; 94 | wire mbuffer_error; 95 | wire mbuffer_full; 96 | wire [MWIDTH-1:0] s2o_m; 97 | 98 | buffer_BB #(MWIDTH, `N*2) mstore 99 | ( 100 | .clk(clk), 101 | .rst_n(rst_n), 102 | .write_strobe(b2s_mnd), 103 | .write_data(b2s_m), 104 | .read_delete(mbuffer_read), 105 | .read_full(mbuffer_full), 106 | .read_data(s2o_m), 107 | .error(mbuffer_error) 108 | ); 109 | 110 | wire [LOG_N-1:0] s2o_addr0; 111 | wire [LOG_N-1:0] s2o_addr1; // not used 112 | wire [WIDTH-1:0] s2o_data0; 113 | wire [WIDTH-1:0] s2o_data1; 114 | wire error_stage; 115 | wire [1:0] s_state; 116 | wire s2o_active; 117 | 118 | stage #(`N, LOG_N, WIDTH) stage_0 119 | ( 120 | .clk(clk), 121 | .rst_n(rst_n), 122 | .in_addr0(b2s_addr0), 123 | .in_addr1(b2s_addr1), 124 | .in_nd(b2s_nd), 125 | .in_data0(b2s_data0), 126 | .in_data1(b2s_data1), 127 | .in_active(b2s_active), 128 | .out_addr0(s2o_addr0), 129 | .out_addr1(s2o_addr1), 130 | .out_data0(s2o_data0), 131 | .out_data1(s2o_data1), 132 | .out_active(s2o_active), 133 | .state(s_state), 134 | .error(error_stage) 135 | ); 136 | 137 | reg s2o_start; 138 | wire s2o_error; 139 | 140 | stage_to_out #(`N, LOG_N, WIDTH, MWIDTH) s2o 141 | ( 142 | .clk(clk), 143 | .rst_n(rst_n), 144 | .start(s2o_start), 145 | .addr(s2o_addr0), 146 | .in_data(s2o_data0), 147 | .out_mread(mbuffer_read), 148 | .in_m(s2o_m), 149 | .out_nd(out_nd), 150 | .out_data(out_data), 151 | .out_m(out_m), 152 | .active(s2o_active), 153 | .error(s2o_error) 154 | ); 155 | 156 | reg writing; 157 | 158 | assign stage_addr0 = (writing)?b2s_addr0:s2o_addr0; 159 | assign stage_addr1 = b2s_addr1; 160 | 161 | // The logic to inititate the reading and writing. 162 | always @ (posedge clk) 163 | if (~rst_n) 164 | begin 165 | writing <= 1'b1; 166 | b2s_start <= 1'b1; 167 | s2o_start <= 1'b0; 168 | end 169 | else 170 | begin 171 | //defaults 172 | b2s_start <= 1'b0; 173 | s2o_start <= 1'b0; 174 | if (s_state == STAGE_EMPTY) 175 | b2s_start <= 1'b1; 176 | else if (s_state == STAGE_FULL) 177 | s2o_start <= 1'b1; 178 | end 179 | 180 | assign error = buffer_error | b2s_error | mbuffer_error | error_stage | s2o_error; 181 | 182 | endmodule -------------------------------------------------------------------------------- /verilog/fft/stage.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A single stage for a FFT. 6 | 7 | module stage 8 | #( 9 | parameter N = 8, 10 | parameter LOG_N = 3, 11 | parameter WIDTH = 32 12 | ) 13 | ( 14 | input wire clk, 15 | input wire rst_n, 16 | // Input to the stage. 17 | input wire [LOG_N-1:0] in_addr0, 18 | input wire [LOG_N-1:0] in_addr1, 19 | input wire in_nd, 20 | input wire [WIDTH-1:0] in_data0, 21 | input wire [WIDTH-1:0] in_data1, 22 | input wire in_active, 23 | // Output from the stage. 24 | input wire [LOG_N-1:0] out_addr0, 25 | input wire [LOG_N-1:0] out_addr1, 26 | output wire [WIDTH-1:0] out_data0, 27 | output wire [WIDTH-1:0] out_data1, 28 | input wire out_active, 29 | output wire [1:0] state, 30 | output reg error 31 | ); 32 | 33 | localparam integer STAGE_EMPTY = 2'd0; 34 | localparam integer STAGE_WRITING = 2'd1; 35 | localparam integer STAGE_FULL = 2'd2; 36 | localparam integer STAGE_READING = 2'd3; 37 | 38 | reg [1:0] state_o; 39 | reg [WIDTH-1:0] RAM[N-1:0]; 40 | 41 | assign out_data0 = RAM[out_addr0]; 42 | assign out_data1 = RAM[out_addr1]; 43 | 44 | assign state = (state_o == STAGE_EMPTY)?((in_active)?STAGE_WRITING:STAGE_EMPTY): 45 | (state_o == STAGE_WRITING)?((~in_active)?STAGE_FULL:STAGE_WRITING): 46 | (state_o == STAGE_FULL)?((out_active)?STAGE_READING:STAGE_FULL): 47 | (~out_active)?STAGE_EMPTY:STAGE_READING; 48 | 49 | always @ (posedge clk) 50 | if (~rst_n) 51 | begin 52 | error <= 1'b0; 53 | state_o <= STAGE_EMPTY; 54 | end 55 | else 56 | begin 57 | if (in_nd) 58 | begin 59 | if (state_o == STAGE_WRITING) 60 | begin 61 | RAM[in_addr0] <= in_data0; 62 | RAM[in_addr1] <= in_data1; 63 | end 64 | end 65 | state_o <= state; 66 | if ((state_o == STAGE_EMPTY) & out_active) 67 | begin 68 | error <= 1'b1; 69 | $display("STAGE_EMPTY got out_active"); 70 | end 71 | else if ((state_o == STAGE_WRITING) & out_active) 72 | begin 73 | error <= 1'b1; 74 | $display("STAGE_WRITING got out_active"); 75 | end 76 | else if ((state_o == STAGE_FULL) & in_active) 77 | begin 78 | error <= 1'b1; 79 | $display("STAGE_FULL got in_active"); 80 | end 81 | else if ((state_o == STAGE_READING) & in_active) 82 | begin 83 | error <= 1'b1; 84 | $display("STAGE_READING got in_active"); 85 | end 86 | end 87 | 88 | endmodule -------------------------------------------------------------------------------- /verilog/fft/stage_to_out.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // Connects a stage and an mstore to a standard block output. 6 | 7 | module stage_to_out 8 | #( 9 | parameter N = 8, 10 | parameter LOG_N = 3, 11 | parameter WIDTH = 32, 12 | parameter MWIDTH = 1 13 | ) 14 | ( 15 | input wire clk, 16 | input wire rst_n, 17 | // Start signals 18 | input wire start, 19 | // From Stage 20 | output reg [LOG_N-1:0] addr, 21 | input wire [WIDTH-1:0] in_data, 22 | // From mStore 23 | output reg out_mread, 24 | input wire in_mfull, 25 | input wire [MWIDTH-1:0] in_m, 26 | // To out 27 | output reg out_nd, 28 | output reg [WIDTH-1:0] out_data, 29 | output reg [MWIDTH-1:0] out_m, 30 | // Finished Signal 31 | output wire active, 32 | output reg error 33 | ); 34 | 35 | reg active_o; 36 | 37 | assign active = active_o | start; 38 | 39 | always @ (posedge clk) 40 | begin 41 | // Set the default values; 42 | out_nd <= 1'b0; 43 | out_mread <= 1'b0; 44 | if (~rst_n) 45 | begin 46 | active_o <= 1'b0; 47 | addr <= {LOG_N{1'b0}}; 48 | error <= 1'b0; 49 | end 50 | else if (start) 51 | begin 52 | if (active_o) 53 | error <= 1'b1; 54 | else 55 | begin 56 | active_o <= 1'b1; 57 | addr <= {LOG_N{1'b0}}; 58 | end 59 | end 60 | else if (active_o) 61 | begin 62 | out_mread <= 1'b1; 63 | out_nd <= 1'b1; 64 | out_data <= in_data; 65 | out_m <= in_m; 66 | if (~in_mfull) 67 | error <=1'b1; 68 | if (addr == N-1) 69 | begin 70 | active_o <= 1'b0; 71 | end 72 | else 73 | addr <= addr + 1; 74 | end 75 | end 76 | endmodule -------------------------------------------------------------------------------- /verilog/fft/stage_to_stage.v.t: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // The connection between two FFT stages. 6 | // Takes care of all the butterfly magic. 7 | 8 | // One module has two modes. 9 | // For example in mode A the connection make be stageW -> stageX 10 | // and in mode B the connection from stageY -> stageZ. 11 | // The is because each connection is only active half the time (A stage 12 | // is either begin written or read to, not both at the same time). 13 | 14 | module stage_to_stage_{{N}} 15 | #( 16 | parameter N = 8, 17 | parameter LOG_N = 3, 18 | parameter WIDTH = 32 19 | ) 20 | (input wire clk, 21 | input wire rst_n, 22 | // Control signals 23 | input wire [LOG_N-1:0] stage_index, 24 | input wire start_A, 25 | input wire start_B, 26 | // Mode A and B 27 | output wire [LOG_N-1:0] from_addr0, 28 | output wire [LOG_N-1:0] from_addr1, 29 | output wire [LOG_N-1:0] to_addr0, 30 | output wire [LOG_N-1:0] to_addr1, 31 | output wire [WIDTH-1:0] to_data0, 32 | output wire [WIDTH-1:0] to_data1, 33 | output wire to_nd, 34 | // Mode A 35 | input wire [WIDTH-1:0] from_data0_A, 36 | input wire [WIDTH-1:0] from_data1_A, 37 | output wire active_A, 38 | // Mode B 39 | input wire [WIDTH-1:0] from_data0_B, 40 | input wire [WIDTH-1:0] from_data1_B, 41 | output wire active_B, 42 | // Other 43 | output wire error 44 | ); 45 | 46 | reg mode; 47 | 48 | // The connection of these depends on the mode. 49 | wire [WIDTH-1:0] from_data0; 50 | wire [WIDTH-1:0] from_data1; 51 | 52 | assign from_data0 = (mode)?from_data0_B:from_data0_A; 53 | assign from_data1 = (mode)?from_data1_B:from_data1_A; 54 | 55 | // Output address send to butterfly 56 | reg [LOG_N-1:0] to_addr0_bf; 57 | wire [LOG_N-1:0] to_addr1_bf; 58 | wire [LOG_N-2:0] tf_addr; 59 | 60 | reg active; 61 | assign active_A = (((~mode) & active) | start_A); 62 | assign active_B = ((mode & active) | start_B); 63 | // Number of series in the stage we are writing to. 64 | reg [LOG_N-1:0] S; 65 | // Contains a 1 for the bits that give j from from_addr0 (i.e. which series). 66 | reg [LOG_N-1:0] series_bits; 67 | 68 | /* 69 | Calculation that determine which positions we should read from and write to 70 | for with the butterfly module. 71 | 72 | If we have a series x_n that we want to get the DFT of, X_k we can write X_k in 73 | terms of E_k and O_k where E_k and O_k are the DFTs of the even and odd components 74 | of x_n respectively. 75 | 76 | for k=N/2 : X_k = E_{k-N/2} - exp(-2*pi*{k-N/2}/N)*O_{k-N/2} 78 | We use this relationship to calculate the DFT of x_n in a series of stages. AFter the 79 | final stage the output is X_k. After the second to last stage the output is an 80 | interleaving of E_k and O_k. 81 | 82 | At some general stage we have S interleaved series. 83 | 84 | So if X_k is the j'th series in a stage and P_n is the n'th output in that stage: 85 | 86 | X_k = P_{k*S+j} 87 | E_k is from a stage with 2*S series and it is in the j'th series in the stage 88 | O_k is from a stage with 2*S series and it is in the (S+j)'th series in stage 89 | Let Q_n be the n'th output of the stage before P. 90 | E_k = Q_{k*2*S+j} 91 | O_k = Q_{k*2*S+S+j} 92 | 93 | Also let T_n = exp(-2*pi*i*n/M) 94 | 95 | M = N*S (total number of items in stage output) 96 | P_{k*S+j} = Q_{2*k*S+j} + T_{k*S} * Q_{k*2*S+S+j} 97 | P_{k*S+j+M/2} = Q_{2*k*S+j} - T_{k*S} * Q_{k*2*S+S+j} 98 | 99 | We'll give these addresses names: 100 | to_addr0 = k*S+j 101 | to_addr1 = k*S+j+M/2 102 | from_addr0 = 2*k*S+j 103 | from_addr1 = 2*k*S+S+j 104 | 105 | Now we assume we know to_addr0 and try to get efficient ways to calculate the 106 | other addresses. 107 | 108 | to_addr0 = k*S+j (j ranges from 0 to S-1, and S is a multiple of two) 109 | If we look at to_addr0 in binary the lowest log2(S) bits give the value of j 110 | and the highest log2(N) bits give the value for k. 111 | */ 112 | 113 | //To get from_addr0 we leave the lowest log2(S) bits alone but we shift the log2(N) 114 | //highest bits to the left (high is to left). 115 | 116 | //To get from_addr1 we add S to from_addr0. 117 | 118 | // to_addr1_bf = out0+addr + M/2 119 | // We simply flip the highest bit from 0 to 1 which adds M/2. 120 | assign to_addr1_bf = {1'b1, to_addr0_bf[LOG_N-2:0]}; 121 | // from_addr0 = 2*k*S+j 122 | // (to_addr0_bf & series_bits) = j 123 | // (to_addr0_bf & ~series_bits) = k*S 124 | // Since the bits don't overlap we can add them with an OR. 125 | assign from_addr0 = (to_addr0_bf & series_bits) | ((to_addr0_bf & ~series_bits)<<1); 126 | assign from_addr1 = from_addr0 + S; 127 | // (to_addr0_bf & ~series_bits) = k*S 128 | assign tf_addr = to_addr0_bf & ~series_bits; 129 | 130 | wire [WIDTH-1:0] tf; 131 | reg tf_addr_nd; 132 | 133 | twiddlefactors_{{N}} twiddlefactors_inst 134 | (.clk (clk), 135 | .addr (tf_addr), 136 | .addr_nd (tf_addr_nd), 137 | .tf_out (tf) 138 | ); 139 | 140 | reg bf_nd; 141 | reg control_error; 142 | reg reading; 143 | 144 | butterfly 145 | #(.MWIDTH (2*LOG_N), 146 | .WIDTH (WIDTH) 147 | ) 148 | butterfly_0 149 | (.clk (clk), 150 | .rst_n (rst_n), 151 | .m_in ({to_addr0_bf, to_addr1_bf}), 152 | .w (tf), 153 | .xa (from_data0), 154 | .xb (from_data1), 155 | .x_nd (bf_nd), 156 | .m_out ({to_addr0, to_addr1}), 157 | .ya (to_data0), 158 | .yb (to_data1), 159 | .y_nd (to_nd) 160 | ); 161 | 162 | always @ (posedge clk) 163 | begin 164 | // Default values. 165 | tf_addr_nd <= 1'b0; 166 | bf_nd <= 1'b0; 167 | if (~rst_n) 168 | begin 169 | mode <= 1'b0; 170 | active <= 1'b0; 171 | reading <= 1'b0; 172 | control_error <= 1'b0; 173 | end 174 | else if (start_A | start_B) 175 | begin 176 | reading <= 1'b1; 177 | active <= 1'b1; 178 | mode <= start_B; 179 | if (start_A & start_B) 180 | control_error <= 1'b1; 181 | if (active | reading) 182 | control_error <= 1'b1; 183 | series_bits <= {LOG_N{1'b1}} >> (stage_index+1); 184 | // In stage_index 0 There are N/2 series. 185 | // There are half as many in each subsequent stage. 186 | S <= {1'b1,{LOG_N-1{1'b0}}} >> stage_index; 187 | to_addr0_bf <= {LOG_N{1'b0}}; 188 | tf_addr_nd <= 1'b1; 189 | end 190 | else if (reading) 191 | begin 192 | // Send data to the butterfly every second clock cycle. 193 | bf_nd <= ~bf_nd; 194 | if (bf_nd) 195 | begin 196 | to_addr0_bf <= to_addr0_bf+1; 197 | tf_addr_nd <= 1'b1; 198 | if (to_addr0_bf == N/2-1) 199 | begin 200 | reading <= 1'b0; 201 | end 202 | end 203 | end 204 | else if (to_nd & (to_addr0 == N/2-1)) 205 | begin 206 | if (reading) 207 | control_error <= 1'b1; 208 | active <= 1'b0; 209 | end 210 | end 211 | 212 | assign error = control_error; 213 | 214 | endmodule 215 | 216 | -------------------------------------------------------------------------------- /verilog/fft/twiddlefactors.v.t: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module twiddlefactors_{{N}} ( 6 | input wire clk, 7 | input wire [{{log_N - 2}}:0] addr, 8 | input wire addr_nd, 9 | output reg signed [{{width-1}}:0] tf_out 10 | ); 11 | 12 | always @ (posedge clk) 13 | begin 14 | if (addr_nd) 15 | begin 16 | case (addr) 17 | {% for tf in tfs %} 18 | {{log_N-1}}'d{{tf.i}}: tf_out <= { {{tf.re_sign}}{{width//2}}'sd{{tf.re}}, {{tf.im_sign}}{{width//2}}'sd{{tf.im}} }; 19 | {% endfor %} 20 | default: 21 | begin 22 | tf_out <= {{width}}'d0; 23 | end 24 | endcase 25 | end 26 | end 27 | endmodule 28 | -------------------------------------------------------------------------------- /verilog/flow/buffer_AA.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // The suffix _AA is meaningless. 6 | 7 | module buffer_AA 8 | #( 9 | parameter WIDTH = 32, 10 | parameter MEM_SIZE = 64, 11 | parameter LOG_MEM_SIZE = 6 12 | ) 13 | ( 14 | input wire clk, 15 | input wire rst_n, 16 | // Write new data. 17 | input wire write_strobe, 18 | input wire [WIDTH-1: 0] write_data, 19 | // Delete the current read data. 20 | input wire read_delete, 21 | // The current read data. 22 | output reg read_full, 23 | output reg [WIDTH-1: 0] read_data, 24 | // Buffer overflow. 25 | output reg write_error, 26 | output reg read_error 27 | ); 28 | 29 | reg [MEM_SIZE-1:0] full; 30 | reg [WIDTH-1: 0] RAM[MEM_SIZE-1:0]; 31 | reg [LOG_MEM_SIZE-1: 0] write_addr; 32 | reg [LOG_MEM_SIZE-1: 0] read_addr; 33 | wire [LOG_MEM_SIZE-1: 0] next_read_addr; 34 | 35 | assign next_read_addr = read_addr + 1; 36 | 37 | always @(posedge clk) 38 | if (!rst_n) 39 | begin 40 | write_error <= 1'b0; 41 | read_error <= 1'b0; 42 | full <= {MEM_SIZE{1'b0}}; 43 | write_addr <= {LOG_MEM_SIZE{1'b0}}; 44 | read_addr <= {LOG_MEM_SIZE{1'b0}}; 45 | end 46 | else 47 | begin 48 | if (write_strobe) 49 | begin 50 | if (!full[write_addr]) 51 | begin 52 | RAM[write_addr] <= write_data; 53 | full[write_addr] <= 1'b1; 54 | write_addr <= write_addr + 1; 55 | end 56 | else 57 | write_error <= 1'b1; 58 | end 59 | if (read_delete) 60 | begin 61 | if (full[read_addr]) 62 | begin 63 | full[read_addr] <= 1'b0; 64 | read_addr <= next_read_addr; 65 | read_full <= full[next_read_addr]; 66 | read_data <= RAM[next_read_addr]; 67 | end 68 | else 69 | begin 70 | read_error <= 1'b1; 71 | read_full <= full[read_addr]; 72 | read_data <= RAM[read_addr]; 73 | end 74 | end 75 | else 76 | begin 77 | read_full <= full[read_addr]; 78 | read_data <= RAM[read_addr]; 79 | end 80 | end 81 | endmodule 82 | 83 | -------------------------------------------------------------------------------- /verilog/flow/buffer_BB.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // This module updates the read_data immediately upon getting 6 | // a delete signal (using assign). 7 | 8 | module buffer_BB 9 | #( 10 | parameter WIDTH = 32, 11 | parameter MEM_SIZE = 64 12 | ) 13 | ( 14 | input wire clk, 15 | input wire rst_n, 16 | // Write new data. 17 | input wire write_strobe, 18 | input wire [WIDTH-1: 0] write_data, 19 | // Delete the current read data. 20 | input wire read_delete, 21 | // The current read data. 22 | output wire read_full, 23 | output wire [WIDTH-1: 0] read_data, 24 | // Buffer overflow. 25 | output wire error 26 | ); 27 | 28 | reg read_error; 29 | reg write_error; 30 | assign error = read_error | write_error; 31 | 32 | function integer clog2; 33 | input integer value; 34 | begin 35 | value = value-1; 36 | for (clog2=0; value>0; clog2=clog2+1) 37 | value = value>>1; 38 | end 39 | endfunction 40 | 41 | localparam integer LOG_MEM_SIZE = clog2(MEM_SIZE); 42 | 43 | reg [MEM_SIZE-1:0] full; 44 | reg [WIDTH-1: 0] RAM[MEM_SIZE-1:0]; 45 | reg [LOG_MEM_SIZE-1: 0] write_addr; 46 | reg [LOG_MEM_SIZE-1: 0] read_addr0; 47 | reg read_full0; 48 | reg read_full1; 49 | reg [WIDTH-1:0] read_data0; 50 | reg [WIDTH-1:0] read_data1; 51 | wire [LOG_MEM_SIZE-1: 0] read_addr1; 52 | wire [LOG_MEM_SIZE-1: 0] read_addr2; 53 | 54 | assign read_addr1 = read_addr0 + 1; 55 | assign read_addr2 = read_addr0 + 2; 56 | 57 | assign read_full = (read_delete)?read_full1:read_full0; 58 | assign read_data = (read_delete)?read_data1:read_data0; 59 | 60 | always @(posedge clk) 61 | if (!rst_n) 62 | begin 63 | write_error <= 1'b0; 64 | read_error <= 1'b0; 65 | full <= {MEM_SIZE{1'b0}}; 66 | write_addr <= {LOG_MEM_SIZE{1'b0}}; 67 | read_addr0 <= {LOG_MEM_SIZE{1'b0}}; 68 | end 69 | else 70 | begin 71 | if (error) 72 | $display("There was an error"); 73 | if (write_strobe) 74 | begin 75 | if (!full[write_addr]) 76 | begin 77 | RAM[write_addr] <= write_data; 78 | full[write_addr] <= 1'b1; 79 | write_addr <= write_addr + 1; 80 | end 81 | else 82 | write_error <= 1'b1; 83 | end 84 | if (read_delete) 85 | begin 86 | if (full[read_addr0]) 87 | begin 88 | full[read_addr0] <= 1'b0; 89 | read_addr0 <= read_addr1; 90 | read_full0 <= full[read_addr1]; 91 | read_data0 <= RAM[read_addr1]; 92 | read_full1 <= full[read_addr2]; 93 | read_data1 <= RAM[read_addr2]; 94 | end 95 | else 96 | begin 97 | read_error <= 1'b1; 98 | read_full0 <= full[read_addr0]; 99 | read_data0 <= RAM[read_addr0]; 100 | read_full1 <= full[read_addr1]; 101 | read_data1 <= RAM[read_addr1]; 102 | end 103 | end 104 | else 105 | begin 106 | read_full0 <= full[read_addr0]; 107 | read_data0 <= RAM[read_addr0]; 108 | read_full1 <= full[read_addr1]; 109 | read_data1 <= RAM[read_addr1]; 110 | end 111 | end 112 | endmodule 113 | 114 | -------------------------------------------------------------------------------- /verilog/flow/dut_split.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is simply a wrapper around the split module 5 | // so that it can be accessed from the myhdl test bench. 6 | 7 | module dut_split; 8 | reg clk; 9 | reg reset; 10 | reg [`WIDTH-1:0] in_data; 11 | wire [`WIDTH*`N_OUT_STREAMS-1:0] out_data; 12 | reg in_nd; 13 | wire out_nd; 14 | wire error; 15 | 16 | wire rst_n; 17 | assign rst_n = ~reset; 18 | initial begin 19 | $from_myhdl(clk, reset, in_data, in_nd); 20 | $to_myhdl(out_data, out_nd, error); 21 | end 22 | 23 | split 24 | #( `N_OUT_STREAMS, `LOG_N_OUT_STREAMS, `WIDTH) 25 | dut 26 | (clk, rst_n, in_data, in_nd, out_data, out_nd); 27 | 28 | endmodule -------------------------------------------------------------------------------- /verilog/flow/qa_buffer_AA.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with a buffer_AA. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WDTH-1:0] out_data, 17 | output reg out_nd 18 | ); 19 | 20 | wire rst_n; 21 | assign rst_n = ~reset; 22 | reg read_delete; 23 | wire read_full; 24 | wire [WDTH-1:0] read_data; 25 | wire write_error; 26 | wire read_error; 27 | 28 | buffer_AA #(WDTH, `BUFFER_LENGTH, `LOG_BUFFER_LENGTH) 29 | the_buffer 30 | (.clk(clk), 31 | .rst_n(rst_n), 32 | .write_strobe(in_nd), 33 | .write_data(in_data), 34 | .read_delete(read_delete), 35 | .read_full(read_full), 36 | .read_data(read_data), 37 | .write_error(write_error), 38 | .read_error(read_error) 39 | ); 40 | 41 | always @ (posedge clk) 42 | begin 43 | if (!rst_n) 44 | begin 45 | read_delete <= 1'b0; 46 | out_data <= {WDTH{1'b0}}; 47 | out_nd <= 1'b0; 48 | end 49 | else 50 | if (write_error) 51 | begin 52 | out_nd <= 1'b1; 53 | out_data <= `WRITEERRORCODE; 54 | read_delete <= 1'b0; 55 | end 56 | else if (read_error) 57 | begin 58 | out_nd <= 1'b1; 59 | out_data <= `READERRORCODE; 60 | read_delete <= 1'b0; 61 | end 62 | else 63 | begin 64 | if (!read_delete && read_full) 65 | begin 66 | read_delete <= 1'b1; 67 | out_nd <= 1'b1; 68 | out_data <= read_data; 69 | end 70 | else 71 | begin 72 | read_delete <= 1'b0; 73 | out_nd <= 1'b0; 74 | end 75 | end 76 | end 77 | 78 | endmodule -------------------------------------------------------------------------------- /verilog/flow/qa_buffer_AA_burst.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with a buffer_AA. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WDTH-1:0] out_data, 17 | output reg out_nd 18 | ); 19 | 20 | wire rst_n; 21 | assign rst_n = ~reset; 22 | reg read_delete; 23 | wire read_full; 24 | wire [WDTH-1:0] read_data; 25 | wire write_error; 26 | wire read_error; 27 | reg [`LOG_BURST_LENGTH-1:0] burst_counter; 28 | 29 | buffer_AA #(WDTH, `BUFFER_LENGTH, `LOG_BUFFER_LENGTH) 30 | the_buffer 31 | (.clk(clk), 32 | .rst_n(rst_n), 33 | .write_strobe(in_nd), 34 | .write_data(in_data), 35 | .read_delete(read_delete), 36 | .read_full(read_full), 37 | .read_data(read_data), 38 | .write_error(write_error), 39 | .read_error(read_error) 40 | ); 41 | 42 | always @ (posedge clk) 43 | begin 44 | if (!rst_n) 45 | begin 46 | read_delete <= 1'b0; 47 | out_data <= {WDTH{1'b0}}; 48 | out_nd <= 1'b0; 49 | burst_counter <= {`LOG_BURST_LENGTH{1'b0}}; 50 | end 51 | else 52 | if (write_error) 53 | begin 54 | out_nd <= 1'b1; 55 | out_data <= `WRITEERRORCODE; 56 | read_delete <= 1'b0; 57 | end 58 | else if (read_error) 59 | begin 60 | out_nd <= 1'b1; 61 | out_data <= `READERRORCODE; 62 | read_delete <= 1'b0; 63 | end 64 | else 65 | begin 66 | if (!read_delete && read_full && !(|burst_counter)) 67 | begin 68 | read_delete <= 1'b1; 69 | out_nd <= 1'b1; 70 | out_data <= read_data; 71 | end 72 | else 73 | begin 74 | if (!read_delete && in_nd) 75 | burst_counter <= burst_counter + 1; 76 | read_delete <= 1'b0; 77 | out_nd <= 1'b0; 78 | end 79 | end 80 | end 81 | 82 | endmodule -------------------------------------------------------------------------------- /verilog/flow/qa_buffer_BB.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with a buffer_BB. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WDTH-1:0] out_data, 17 | output reg out_nd 18 | ); 19 | 20 | wire rst_n; 21 | assign rst_n = ~reset; 22 | reg read_delete; 23 | wire read_full; 24 | wire [WDTH-1:0] read_data; 25 | wire write_error; 26 | wire read_error; 27 | 28 | buffer_BB #(WDTH, `BUFFER_LENGTH, `LOG_BUFFER_LENGTH) 29 | the_buffer 30 | (.clk(clk), 31 | .rst_n(rst_n), 32 | .write_strobe(in_nd), 33 | .write_data(in_data), 34 | .read_delete(read_delete), 35 | .read_full(read_full), 36 | .read_data(read_data), 37 | .write_error(write_error), 38 | .read_error(read_error) 39 | ); 40 | 41 | always @ (posedge clk) 42 | begin 43 | if (!rst_n) 44 | begin 45 | read_delete <= 1'b0; 46 | out_data <= {WDTH{1'b0}}; 47 | out_nd <= 1'b0; 48 | end 49 | else 50 | if (write_error) 51 | begin 52 | out_nd <= 1'b1; 53 | out_data <= `WRITEERRORCODE; 54 | read_delete <= 1'b0; 55 | end 56 | else if (read_error) 57 | begin 58 | out_nd <= 1'b1; 59 | out_data <= `READERRORCODE; 60 | read_delete <= 1'b0; 61 | end 62 | else 63 | begin 64 | if (read_full) 65 | begin 66 | read_delete <= 1'b1; 67 | out_nd <= 1'b1; 68 | out_data <= read_data; 69 | end 70 | else 71 | begin 72 | read_delete <= 1'b0; 73 | out_nd <= 1'b0; 74 | end 75 | end 76 | end 77 | 78 | endmodule -------------------------------------------------------------------------------- /verilog/flow/qa_split.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with a splitter. Only the first stream is returned. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output wire [WDTH-1:0] out_data, 17 | output wire out_nd 18 | ); 19 | 20 | // Separate the input stream into a sample stream and a message stream. 21 | 22 | wire [WDTH-1:0] null_data; 23 | 24 | wire rst_n; 25 | assign rst_n = ~reset; 26 | 27 | split #(2, 1) split_0 28 | (.clk(clk), 29 | .rst_n(rst_n), 30 | .in_data(in_data), 31 | .in_nd(in_nd), 32 | .out_data({null_data, out_data}), 33 | .out_nd(out_nd) 34 | ); 35 | 36 | endmodule -------------------------------------------------------------------------------- /verilog/flow/split.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module split 6 | #( 7 | parameter N_OUT_STREAMS = 2, 8 | parameter LOG_N_OUT_STREAMS = 1, 9 | parameter WIDTH = 32 10 | ) 11 | ( 12 | input clk, 13 | input rst_n, 14 | input wire [WIDTH-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WIDTH*N_OUT_STREAMS-1:0] out_data, 17 | output reg out_nd 18 | ); 19 | 20 | reg [LOG_N_OUT_STREAMS-1:0] pos; 21 | wire [WIDTH*N_OUT_STREAMS-1:0] shifted_data; 22 | 23 | assign shifted_data = in_data << WIDTH*pos; 24 | 25 | always @ (posedge clk) 26 | begin 27 | if (!rst_n) 28 | begin 29 | pos <= {LOG_N_OUT_STREAMS{1'b0}}; 30 | end 31 | else 32 | if (in_nd) 33 | begin 34 | if (pos == N_OUT_STREAMS-1) 35 | begin 36 | out_data <= out_data + shifted_data; 37 | pos <= 1'b0; 38 | out_nd <= 1'b1; 39 | end 40 | else if (pos == 0) 41 | begin 42 | out_data <= shifted_data; 43 | out_nd <= 1'b0; 44 | pos <= pos + 1; 45 | end 46 | else 47 | begin 48 | out_data <= out_data + shifted_data; 49 | out_nd <= 1'b0; 50 | pos <= pos + 1; 51 | end 52 | end 53 | else 54 | out_nd <= 1'b0; 55 | end 56 | 57 | endmodule 58 | -------------------------------------------------------------------------------- /verilog/flter/filter.v.t: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module filter 6 | #( 7 | parameter WIDTH = 32, 8 | parameter MWIDTH = 1, 9 | parameter FLTLEN = 10, 10 | // ID is used to work out whether messages are 11 | // directed to it. 12 | parameter ID = 0 13 | ) 14 | ( 15 | input wire clk, 16 | input wire rst_n, 17 | input wire [WIDTH-1:0] in_data, 18 | input wire in_nd, 19 | input wire [MWIDTH-1:0] in_m, 20 | // Takes input messages to set taps. 21 | input wire [`MSG_WIDTH-1:0] in_msg, 22 | input wire in_msg_nd, 23 | output wire [WIDTH-1:0] out_data, 24 | output wire out_nd, 25 | output wire [MWIDTH-1:0] out_m, 26 | output wire [`MSG_WIDTH-1:0] out_msg, 27 | output wire out_msg_nd, 28 | output wire error 29 | ); 30 | 31 | function integer clog2; 32 | input integer value; 33 | begin 34 | value = value-1; 35 | for (clog2=0; value>0; clog2=clog2+1) 36 | value = value>>1; 37 | end 38 | endfunction 39 | 40 | localparam integer LOG_FLTLEN = clog2(FLTLEN); 41 | 42 | reg [WIDTH*(FLTLEN-1)-1:0] history; 43 | reg in_nd_old; 44 | reg [WIDTH*(FLTLEN-1)-1:0] shifted_history; 45 | wire in_first_filter; 46 | 47 | // Tap values are set by the in_msg and in_msg_nd wires. 48 | // We assume that WIDTH+1=MSG_WIDTH are the same. 49 | reg [LOG_FLTLEN-1:0] which_pos; 50 | reg setting_taps; 51 | reg [WIDTH/2-1:0] tapvalues[FLTLEN-1:0]; 52 | wire signed [`MSG_WIDTH-1:0] signed_msg; 53 | wire signed [WIDTH/2-1:0] small_signed_msg; 54 | 55 | wire summult_error; 56 | reg tap_set_error; 57 | assign error = summult_error | tap_set_error; 58 | 59 | assign signed_msg = in_msg; 60 | assign small_signed_msg = signed_msg; 61 | 62 | genvar i; 63 | generate 64 | for (i=0; i0; log2=log2+1) 9 | value = value>>1; 10 | end 11 | endfunction 12 | -------------------------------------------------------------------------------- /verilog/fpgamath/multiply.v: -------------------------------------------------------------------------------- 1 | 2 | `ifndef XILINX 3 | 4 | module MULT18X18S 5 | // The module was copied from the Ettus UHD code. 6 | (output reg signed [35:0] P, 7 | input signed [17:0] A, 8 | input signed [17:0] B, 9 | input C, // Clock 10 | input CE, // Clock Enable 11 | input R // Synchronous Reset 12 | ); 13 | 14 | always @(posedge C) 15 | if(R) 16 | P <= 36'sd0; 17 | else if(CE) 18 | begin 19 | P <= A * B; 20 | end 21 | endmodule 22 | 23 | `endif 24 | 25 | module multiply 26 | #( 27 | parameter WDTH = 0 28 | ) 29 | ( 30 | input wire clk, 31 | input wire rst_n, 32 | input wire signed [WDTH-1:0] x, 33 | input wire signed [WDTH-1:0] y, 34 | output wire signed [WDTH-1:0] z 35 | ); 36 | 37 | reg ce; 38 | initial 39 | ce <= 1'b1; 40 | 41 | wire signed [17:0] xb; 42 | wire signed [17:0] yb; 43 | assign xb = x; 44 | assign yb = y; 45 | wire signed [35:0] xy; 46 | 47 | MULT18X18S multer (.P(xy), .A(xb), .B(yb), .C(clk), .CE(ce), .R(~rst_n)); 48 | 49 | assign z = xy >>> (WDTH-1); 50 | 51 | always @ (posedge clk) 52 | begin 53 | //xy <= xb * yb; 54 | //z <= xy >>> (WDTH-2); 55 | end 56 | 57 | endmodule 58 | 59 | 60 | -------------------------------------------------------------------------------- /verilog/fpgamath/multiply_complex.v: -------------------------------------------------------------------------------- 1 | // Answers are divided by 2. 2 | 3 | module multiply_complex 4 | #( 5 | parameter WIDTH = 32 6 | ) 7 | ( 8 | input wire clk, 9 | input wire rst_n, 10 | input wire signed [WIDTH-1:0] x, 11 | input wire signed [WIDTH-1:0] y, 12 | output wire signed [WIDTH-1:0] z 13 | ); 14 | 15 | wire signed [WIDTH/2-1:0] x_re; 16 | wire signed [WIDTH/2-1:0] x_im; 17 | wire signed [WIDTH/2-1:0] y_re; 18 | wire signed [WIDTH/2-1:0] y_im; 19 | assign x_re = x[WIDTH-1:WIDTH/2]; 20 | assign x_im = x[WIDTH/2-1:0]; 21 | assign y_re = y[WIDTH-1:WIDTH/2]; 22 | assign y_im = y[WIDTH/2-1:0]; 23 | 24 | wire signed [WIDTH/2-1:0] xreyre; 25 | wire signed [WIDTH/2-1:0] xreyim; 26 | wire signed [WIDTH/2-1:0] ximyre; 27 | wire signed [WIDTH/2-1:0] ximyim; 28 | 29 | wire signed [WIDTH/2:0] z_re_l; 30 | wire signed [WIDTH/2:0] z_im_l; 31 | wire signed [WIDTH/2-1:0] z_re; 32 | wire signed [WIDTH/2-1:0] z_im; 33 | 34 | assign z_re_l = xreyre - ximyim; 35 | assign z_im_l = xreyim + ximyre; 36 | assign z_re = z_re_l >> 1; 37 | assign z_im = z_im_l >> 1; 38 | assign z = {z_re, z_im}; 39 | 40 | multiply #(WIDTH/2) multiply_0 41 | (.clk(clk), 42 | .rst_n(rst_n), 43 | .x(x_re), 44 | .y(y_re), 45 | .z(xreyre) 46 | ); 47 | 48 | multiply #(WIDTH/2) multiply_1 49 | (.clk(clk), 50 | .rst_n(rst_n), 51 | .x(x_re), 52 | .y(y_im), 53 | .z(xreyim) 54 | ); 55 | 56 | multiply #(WIDTH/2) multiply_2 57 | (.clk(clk), 58 | .rst_n(rst_n), 59 | .x(x_im), 60 | .y(y_re), 61 | .z(ximyre) 62 | ); 63 | 64 | multiply #(WIDTH/2) multiply_3 65 | (.clk(clk), 66 | .rst_n(rst_n), 67 | .x(x_im), 68 | .y(y_im), 69 | .z(ximyim) 70 | ); 71 | 72 | endmodule 73 | 74 | 75 | -------------------------------------------------------------------------------- /verilog/fpgamath/qa_multiply.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module qa_contents 6 | #( 7 | parameter WIDTH = 32, 8 | parameter MWIDTH = 1 9 | ) 10 | ( 11 | input wire clk, 12 | input wire rst_n, 13 | input wire [WIDTH-1:0] in_data, 14 | input wire in_nd, 15 | input wire [MWIDTH-1:0] in_m, 16 | input wire [`MSG_WIDTH-1:0] in_msg, 17 | input wire in_msg_nd, 18 | output wire [WIDTH-1:0] out_data, 19 | output reg out_nd, 20 | output reg [MWIDTH-1:0] out_m, 21 | output wire [`MSG_WIDTH-1:0] out_msg, 22 | output wire out_msg_nd, 23 | output reg error 24 | ); 25 | 26 | wire [WIDTH/2-1:0] x; 27 | wire [WIDTH/2-1:0] y; 28 | wire [WIDTH/2-1:0] z; 29 | assign x = in_data[WIDTH-1:WIDTH/2]; 30 | assign y = in_data[WIDTH/2-1:0]; 31 | assign out_data = {{WIDTH/2{1'b0}}, z}; 32 | 33 | always @ (posedge clk) 34 | if (~rst_n) 35 | error <= 1'b0; 36 | else 37 | begin 38 | out_nd <= in_nd; 39 | end 40 | 41 | multiply #(WIDTH/2) multiply_0 42 | (.clk(clk), 43 | .rst_n(rst_n), 44 | .x(x), 45 | .y(y), 46 | .z(z) 47 | ); 48 | 49 | endmodule -------------------------------------------------------------------------------- /verilog/fpgamath/qa_multiply_complex.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module qa_contents 6 | #( 7 | parameter WIDTH = 32, 8 | parameter MWIDTH = 1 9 | ) 10 | ( 11 | input wire clk, 12 | input wire rst_n, 13 | input wire [WIDTH-1:0] in_data, 14 | input wire in_nd, 15 | input wire [MWIDTH-1:0] in_m, 16 | input wire [`MSG_WIDTH-1:0] in_msg, 17 | input wire in_msg_nd, 18 | output wire [WIDTH-1:0] out_data, 19 | output reg out_nd, 20 | output reg [MWIDTH-1:0] out_m, 21 | output wire [`MSG_WIDTH-1:0] out_msg, 22 | output wire out_msg_nd, 23 | output reg error 24 | ); 25 | 26 | reg [WIDTH-1:0] x; 27 | reg counter; 28 | reg active; 29 | 30 | always @ (posedge clk) 31 | begin 32 | out_nd <= 1'b0; 33 | if (~rst_n) 34 | begin 35 | active <= 1'b0; 36 | counter <= 1'b0; 37 | error <= 1'b0; 38 | end 39 | else if (in_nd) 40 | begin 41 | if (((~active)& (in_data != {WIDTH{1'b0}})) | (counter == 1'd0)) 42 | begin 43 | active <= 1'b1; 44 | x <= in_data; 45 | counter <= 1'b1; 46 | end 47 | else if (counter == 1'b1) 48 | begin 49 | counter <= 1'b0; 50 | out_nd <= 1'b1; 51 | end 52 | end 53 | end 54 | 55 | multiply_complex #(WIDTH) multiply_complex_0 56 | (.clk(clk), 57 | .rst_n(rst_n), 58 | .x(x), 59 | .y(in_data), 60 | .z(out_data) 61 | ); 62 | 63 | endmodule -------------------------------------------------------------------------------- /verilog/message/dut_message_slicer.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is simply a wrapper around the message_stream_combiner module 5 | // so that it can be accessed from the myhdl test bench. 6 | 7 | module dut_message_slicer; 8 | reg clk; 9 | reg reset; 10 | reg [`WIDTH*`N_SLICES-1:0] in_data; 11 | wire [`WIDTH-1:0] out_data; 12 | reg in_nd; 13 | wire out_nd; 14 | wire error; 15 | 16 | initial begin 17 | $from_myhdl(clk, reset, in_data, in_nd); 18 | $to_myhdl(out_data, out_nd, error); 19 | end 20 | 21 | wire rst_n; 22 | assign rst_n = ~reset; 23 | 24 | message_slicer 25 | #(`N_SLICES, `WIDTH, `BUFFER_LENGTH) 26 | dut 27 | (clk, rst_n, in_data, in_nd, out_data, out_nd, error); 28 | 29 | endmodule -------------------------------------------------------------------------------- /verilog/message/dut_message_stream_combiner.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is simply a wrapper around the message_stream_combiner module 5 | // so that it can be accessed from the myhdl test bench. 6 | 7 | module dut_message_stream_combiner; 8 | reg clk; 9 | reg reset; 10 | reg [`WIDTH*`N_STREAMS-1:0] in_data; 11 | wire [`WIDTH-1:0] out_data; 12 | reg [`N_STREAMS-1:0] in_nd; 13 | wire out_nd; 14 | wire error; 15 | 16 | wire rst_n; 17 | assign rst_n = ~reset; 18 | 19 | initial begin 20 | $from_myhdl(clk, reset, in_data, in_nd); 21 | $to_myhdl(out_data, out_nd, error); 22 | end 23 | 24 | message_stream_combiner 25 | #( `N_STREAMS, `LOG_N_STREAMS, `WIDTH, 26 | `INPUT_BUFFER_LENGTH, `LOG_INPUT_BUFFER_LENGTH, 27 | `MAX_PACKET_LENGTH, `LOG_MAX_PACKET_LENGTH) 28 | dut 29 | (clk, rst_n, in_data, in_nd, out_data, out_nd, error); 30 | 31 | endmodule -------------------------------------------------------------------------------- /verilog/message/message_slicer.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module message_slicer 6 | #( 7 | parameter N_SLICES = 2, 8 | parameter WIDTH = 32, 9 | parameter BUFFER_LENGTH = 32 10 | ) 11 | ( 12 | input clk, 13 | input rst_n, 14 | input wire [WIDTH*N_SLICES-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WIDTH-1:0] out_data, 17 | output reg out_nd, 18 | output wire error 19 | ); 20 | 21 | function integer clog2; 22 | input integer value; 23 | begin 24 | value = value-1; 25 | for (clog2=0; value>0; clog2=clog2+1) 26 | value = value>>1; 27 | end 28 | endfunction 29 | 30 | localparam integer LOG_N_SLICES = clog2(N_SLICES); 31 | 32 | reg read_delete; 33 | wire [N_SLICES*WIDTH-1:0] read_data; 34 | wire read_full; 35 | 36 | buffer_BB #(N_SLICES*WIDTH, BUFFER_LENGTH) buffer_0 37 | (.clk(clk), 38 | .rst_n(rst_n), 39 | .write_strobe(in_nd), 40 | .write_data(in_data), 41 | .read_delete(read_delete), 42 | .read_full(read_full), 43 | .read_data(read_data), 44 | .error(error) 45 | ); 46 | 47 | reg [LOG_N_SLICES-1:0] pos; 48 | wire [WIDTH-1:0] sliced_data[N_SLICES-1:0]; 49 | 50 | genvar i; 51 | generate 52 | for(i=0; i> (WIDTH-1); 72 | assign is_header = temp_is_header; 73 | 74 | // Deal with reading from input buffers. 75 | always @ (posedge clk) 76 | begin 77 | if (!rst_n) 78 | begin 79 | stream <= {LOG_N_STREAMS{1'b0}}; 80 | read_deletes <= {N_STREAMS{1'b0}}; 81 | packet_pos <= {LOG_MAX_PACKET_LENGTH{1'b0}}; 82 | packet_length <= {LOG_MAX_PACKET_LENGTH{1'b0}}; 83 | end 84 | else 85 | begin 86 | // If just deleted then we need to wait a cycle before the 87 | // buffer displays the new value for reading. 88 | if ((!read_deletes[stream]) && (read_fulls[stream])) 89 | begin 90 | read_deletes <= {{N_STREAMS-1{1'b0}},{1'b1}} << stream; 91 | out_nd <= 1'b1; 92 | out_data <= read_datas[stream]; 93 | if (packet_pos == 0) 94 | begin 95 | // Check if header (look at header bit) 96 | if (is_header) 97 | begin 98 | packet_length <= read_datas[stream][WIDTH-2 -:LOG_MAX_PACKET_LENGTH]; 99 | if (read_datas[stream][WIDTH-2 -:LOG_MAX_PACKET_LENGTH] != 0) 100 | packet_pos <= packet_pos + 1; 101 | end 102 | end // if (packet_pos == 0) 103 | else 104 | begin 105 | if (packet_pos == packet_length) 106 | packet_pos <= 0; 107 | else 108 | packet_pos <= packet_pos + 1; 109 | end 110 | end 111 | else 112 | begin 113 | out_nd <= 1'b0; 114 | if (packet_pos == 0) 115 | // Move onto next stream. 116 | begin 117 | if (stream == N_STREAMS-1) 118 | stream <= 0; 119 | else 120 | stream <= stream + 1; 121 | end 122 | read_deletes <= {N_STREAMS{1'b0}}; 123 | end 124 | end 125 | end 126 | 127 | endmodule 128 | -------------------------------------------------------------------------------- /verilog/message/qa_combo.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with only the sample_msg_splitter module to test. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output wire [WDTH-1:0] out_data, 17 | output wire out_nd 18 | ); 19 | 20 | // Separate the input stream into a sample stream and a message stream. 21 | 22 | wire [WDTH-1:0] before_data; 23 | wire [WDTH-1:0] after_data; 24 | wire [WDTH-1:0] before_samples_shifted; 25 | wire [WDTH-1:0] after_samples_shifted; 26 | wire before_nd; 27 | wire after_nd; 28 | wire [`MSG_WIDTH-1:0] before_msg; 29 | wire before_msg_nd; 30 | wire [`MSG_WIDTH-1:0] after_msg; 31 | wire after_msg_nd; 32 | wire splitter_error; 33 | 34 | wire rst_n; 35 | assign rst_n = ~reset; 36 | 37 | sample_msg_splitter #(WDTH) sample_msg_splitter_0 38 | (.clk(clk), 39 | .rst_n(rst_n), 40 | .in_data(in_data), 41 | .in_nd(in_nd), 42 | .out_samples(before_data), 43 | .out_samples_nd(before_nd), 44 | .out_msg(before_msg), 45 | .out_msg_nd(before_msg_nd), 46 | .error(splitter_error) 47 | ); 48 | 49 | assign after_nd = before_nd; 50 | assign after_msg_nd = before_msg_nd; 51 | assign after_msg = before_msg; 52 | assign after_samples_shifted = before_data; 53 | //assign before_samples_shifted = {before_data[WDTH-3:WDTH/2-1], 1'b0, before_data[WDTH/2-2: 0], 1'b0}; 54 | //assign after_data = before_samples_shifted; 55 | //assign after_samples_shifted = {2'b0, after_data[WDTH-1 :WDTH/2+1], after_data[WDTH/2-1 :1]}; 56 | 57 | message_stream_combiner #(2, 1, WDTH, `COMBINER_BUFFER_LENGTH, `LOG_COMBINER_BUFFER_LENGTH, `MAX_PACKET_LENGTH, `MSG_LENGTH_WIDTH) message_stream_combiner_0 58 | (.clk(clk), 59 | .rst_n(rst_n), 60 | .in_data({after_samples_shifted, after_msg}), 61 | .in_nd({after_nd, after_msg_nd}), 62 | .out_data(out_data), 63 | .out_nd(out_nd), 64 | .error(combiner_error) 65 | ); 66 | 67 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_debug.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // Testing sending back debug messages. 6 | 7 | module qa_contents 8 | #( 9 | parameter WIDTH = 32, 10 | parameter MWIDTH = 1 11 | ) 12 | ( 13 | input wire clk, 14 | input wire rst_n, 15 | input wire [WIDTH-1:0] in_data, 16 | input wire in_nd, 17 | input wire [MWIDTH-1:0] in_m, 18 | input wire [`MSG_WIDTH-1:0] in_msg, 19 | input wire in_msg_nd, 20 | output wire [WIDTH-1:0] out_data, 21 | output wire out_nd, 22 | output wire [MWIDTH-1:0] out_m, 23 | output wire [`MSG_WIDTH-1:0] out_msg, 24 | output wire out_msg_nd, 25 | output wire error 26 | ); 27 | 28 | localparam integer MSGLENGTH = 2; 29 | reg [MSGLENGTH*WIDTH-1:0] msg; 30 | reg msg_nd; 31 | wire [WIDTH-1:0] small_data; 32 | smaller #(WIDTH) smaller_0 (in_data, small_data); 33 | 34 | assign out_nd = in_nd; 35 | assign out_m = in_m; 36 | assign out_data = in_data; 37 | 38 | always @ (posedge clk) 39 | begin 40 | msg_nd <= 1'b0; 41 | if (~rst_n) 42 | begin 43 | end 44 | else if (in_nd) 45 | begin 46 | // If divisible by four send an error message. 47 | // Not if it is 0. 48 | if ((in_data[1:0] == 2'b0) & (|in_data)) 49 | begin 50 | msg <= {1'b1, 10'd1, 21'd0, small_data}; 51 | msg_nd <= 1'b1; 52 | end 53 | end 54 | end 55 | 56 | message_slicer #(MSGLENGTH, WIDTH, 48) message_slicer_0 57 | (clk, rst_n, msg, msg_nd, out_msg, out_msg_nd, error); 58 | 59 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_message_stream_combiner_bits.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with only the message_stream_combiner. 6 | // The input stream and an empty stream go to the combiner. 7 | 8 | module qa_wrapper 9 | #( 10 | parameter WIDTH = 32 11 | ) 12 | ( 13 | input wire clk, 14 | input wire reset, 15 | input wire [WIDTH-1:0] in_data, 16 | input wire in_nd, 17 | output wire [WIDTH-1:0] out_data, 18 | output wire out_nd 19 | ); 20 | 21 | reg empty_nd; 22 | reg [WIDTH-1:0] empty_data; 23 | wire mid_nd; 24 | wire [WIDTH-1:0] mid_data; 25 | wire combiner_error; 26 | wire bits_error; 27 | 28 | always @ (posedge clk) 29 | if (reset) 30 | begin 31 | empty_nd <= 1'b0; 32 | empty_data <= 12; 33 | end 34 | 35 | assign rst_n = ~reset; 36 | 37 | message_stream_combiner #(2, 1, WIDTH, `COMBINER_BUFFER_LENGTH, `LOG_COMBINER_BUFFER_LENGTH, `MAX_PACKET_LENGTH, `MSG_LENGTH_WIDTH) message_stream_combiner_0 38 | (.clk(clk), 39 | .rst_n(rst_n), 40 | .in_data({in_data, empty_data}), 41 | .in_nd({in_nd, empty_nd}), 42 | .out_data(mid_data), 43 | .out_nd(mid_nd), 44 | .error(combiner_error) 45 | ); 46 | 47 | bits #(WIDTH) bits_0 48 | (.clk(clk), 49 | .reset(reset), 50 | .in_data(mid_data), 51 | .in_nd(mid_nd), 52 | .out_data(out_data), 53 | .out_nd(out_nd), 54 | .error(bits_error) 55 | ); 56 | 57 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_message_stream_combiner_one.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with only the message_stream_combiner. 6 | // The input stream and an empty stream go to the combiner. 7 | 8 | module qa_wrapper 9 | #( 10 | parameter WDTH = 32 11 | ) 12 | ( 13 | input wire clk, 14 | input wire reset, 15 | input wire [WDTH-1:0] in_data, 16 | input wire in_nd, 17 | output wire [WDTH-1:0] out_data, 18 | output wire out_nd 19 | ); 20 | 21 | reg empty_nd; 22 | reg [WDTH-1:0] empty_data; 23 | wire combiner_error; 24 | 25 | initial 26 | begin 27 | empty_nd <= 1'b0; 28 | empty_data <= 12; 29 | end 30 | 31 | assign rst_n = ~reset; 32 | 33 | message_stream_combiner #(2, 1, WDTH, `COMBINER_BUFFER_LENGTH, `LOG_COMBINER_BUFFER_LENGTH, `MAX_PACKET_LENGTH, `MSG_LENGTH_WIDTH) message_stream_combiner_0 34 | (.clk(clk), 35 | .rst_n(rst_n), 36 | .in_data({in_data, empty_data}), 37 | .in_nd({in_nd, empty_nd}), 38 | .out_data(out_data), 39 | .out_nd(out_nd), 40 | .error(combiner_error) 41 | ); 42 | 43 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_sample_msg_splitter.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with only the sample_msg_splitter module to test. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output wire [WDTH-1:0] out_data, 17 | output wire out_nd 18 | ); 19 | 20 | // Separate the input stream into a sample stream and a message stream. 21 | 22 | wire [`MSG_WIDTH-1:0] out_msg; 23 | wire out_msg_nd; 24 | wire splitter_error; 25 | 26 | wire rst_n; 27 | assign rst_n = ~reset; 28 | 29 | sample_msg_splitter #(WDTH) sample_msg_splitter_0 30 | (.clk(clk), 31 | .rst_n(rst_n), 32 | .in_data(in_data), 33 | .in_nd(in_nd), 34 | .out_samples(out_data), 35 | .out_samples_nd(out_nd), 36 | .out_msg(out_msg), 37 | .out_msg_nd(out_msg_nd), 38 | .error(splitter_error) 39 | ); 40 | 41 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_sample_msg_splitter_returns_msgs.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with only the sample_msg_splitter module to test. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output wire [WDTH-1:0] out_data, 17 | output wire out_nd 18 | ); 19 | 20 | // Separate the input stream into a sample stream and a message stream. 21 | 22 | wire [WDTH-1:0] discard_data; 23 | wire discard_nd; 24 | wire splitter_error; 25 | 26 | wire rst_n; 27 | assign rst_n = ~reset; 28 | 29 | sample_msg_splitter #(WDTH) sample_msg_splitter_0 30 | (.clk(clk), 31 | .rst_n(rst_n), 32 | .in_data(in_data), 33 | .in_nd(in_nd), 34 | .out_samples(discard_data), 35 | .out_samples_nd(discard_nd), 36 | .out_msg(out_data), 37 | .out_msg_nd(out_nd), 38 | .error(splitter_error) 39 | ); 40 | 41 | endmodule -------------------------------------------------------------------------------- /verilog/message/qa_splitcombiner.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // A qa_wrapper with a splitter and message_stream_combiner 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output reg [WDTH-1:0] out_data, 17 | output reg out_nd 18 | ); 19 | 20 | // Separate the input stream into a sample stream and a message stream. 21 | 22 | wire [WDTH-1:0] mid1_data; 23 | wire [WDTH-1:0] mid2_data; 24 | wire [WDTH-1:0] staged_data; 25 | wire staged_nd; 26 | wire mid_nd; 27 | wire splitter_error; 28 | 29 | wire rst_n; 30 | assign rst_n = ~reset; 31 | 32 | split #(2, 1) split_0 33 | (.clk(clk), 34 | .rst_n(rst_n), 35 | .in_data(in_data), 36 | .in_nd(in_nd), 37 | .out_data({mid1_data, mid2_data}), 38 | .out_nd(mid_nd) 39 | ); 40 | 41 | message_stream_combiner 42 | #(2, 1, WDTH, `COMBINER_BUFFER_LENGTH, `LOG_COMBINER_BUFFER_LENGTH, 43 | `MAX_PACKET_LENGTH, `MSG_LENGTH_WIDTH) 44 | message_stream_combiner_0 45 | (.clk(clk), 46 | .rst_n(rst_n), 47 | .in_data({mid1_data, mid2_data}), 48 | .in_nd({mid_nd, mid_nd}), 49 | .out_data(staged_data), 50 | .out_nd(staged_nd), 51 | .error(combiner_error) 52 | ); 53 | 54 | always @ (posedge clk) 55 | begin 56 | if (combiner_error) 57 | begin 58 | out_nd <= 1'b1; 59 | out_data <= `ERRORCODE; 60 | end 61 | else 62 | begin 63 | out_nd <= staged_nd; 64 | out_data <= staged_data; 65 | end 66 | end 67 | 68 | endmodule -------------------------------------------------------------------------------- /verilog/message/sample_msg_splitter.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // An input stream is a mix of samples and messages. 6 | // The first bit of a message header is 1. 7 | // Samples and message contents have a first bit of 0. 8 | // A message header says how many message contents follow. 9 | 10 | // The stream is split into a sample stream and a message stream. 11 | 12 | module sample_msg_splitter 13 | #( 14 | parameter WIDTH = 32 15 | ) 16 | ( 17 | input clk, 18 | input rst_n, 19 | input wire [WIDTH-1:0] in_data, 20 | input wire in_nd, 21 | output reg [WIDTH-1:0] out_samples, 22 | output reg out_samples_nd, 23 | output reg [WIDTH-1:0] out_msg, 24 | output reg out_msg_nd, 25 | output reg error 26 | ); 27 | 28 | // Control stuff 29 | reg [`MSG_LENGTH_WIDTH-1:0] packet_pos; 30 | reg [`MSG_LENGTH_WIDTH-1:0] packet_length; 31 | 32 | always @ (posedge clk) 33 | begin 34 | if (!rst_n) 35 | begin 36 | out_samples_nd <= 1'b0; 37 | out_msg_nd <= 1'b0; 38 | packet_pos <= {`MSG_LENGTH_WIDTH{1'b0}}; 39 | packet_length <= {`MSG_LENGTH_WIDTH{1'b0}}; 40 | error <= 1'b0; 41 | end 42 | else 43 | if (in_nd) 44 | begin 45 | if (in_data[WIDTH-1]) 46 | begin 47 | // In a header. 48 | packet_length <= in_data[WIDTH-2 -:`MSG_LENGTH_WIDTH]; 49 | if (in_data[WIDTH-2 -:`MSG_LENGTH_WIDTH] == 0) 50 | packet_pos <= 0; 51 | else 52 | packet_pos <= 1; 53 | out_msg <= in_data; 54 | out_msg_nd <= 1'b1; 55 | out_samples_nd <= 1'b0; 56 | // Already in a packet but we got a header -> error. 57 | if (packet_pos != 0) 58 | error <= 1'b1; 59 | end 60 | else 61 | begin 62 | if (packet_pos == 0) 63 | begin 64 | out_samples <= in_data; 65 | out_samples_nd <= 1'b1; 66 | out_msg_nd <= 1'b0; 67 | end 68 | else 69 | begin 70 | if (packet_pos == packet_length) 71 | begin 72 | packet_pos <= {`MSG_LENGTH_WIDTH{1'b0}}; 73 | packet_length <= {`MSG_LENGTH_WIDTH{1'b0}}; 74 | end 75 | else 76 | packet_pos <= packet_pos + 1; 77 | out_msg <= in_data; 78 | out_msg_nd <= 1'b1; 79 | out_samples_nd <= 1'b0; 80 | end 81 | end 82 | end 83 | else 84 | begin 85 | out_samples_nd <= 1'b0; 86 | out_msg_nd <= 1'b0; 87 | end 88 | end 89 | 90 | endmodule 91 | -------------------------------------------------------------------------------- /verilog/message/smaller.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module smaller #(parameter WIDTH = 32) 6 | (input wire [WIDTH-1:0] in_val, 7 | output wire [WIDTH-1:0] out_val 8 | ); 9 | wire signed [WIDTH/2-1:0] val_re; 10 | wire signed [WIDTH/2-1:0] val_im; 11 | wire signed [WIDTH/2-1:0] val_re_i; 12 | wire signed [WIDTH/2-1:0] val_im_i; 13 | wire signed [WIDTH/2-2:0] val_re_s; 14 | wire signed [WIDTH/2-2:0] val_im_s; 15 | assign val_re = in_val[WIDTH-1:WIDTH/2]; 16 | assign val_im = in_val[WIDTH/2-1:0]; 17 | assign val_re_i = val_re/2; 18 | assign val_im_i = val_im/2; 19 | assign val_re_s = val_re_i; 20 | assign val_im_s = val_im_i; 21 | assign out_val = {2'b0, val_re_s, val_im_s}; 22 | endmodule 23 | 24 | -------------------------------------------------------------------------------- /verilog/uhd/bits.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // This qa_wrapper takes the data and outputs alternately 6 | // bit position and bit contents. 7 | // If data arrives too frequently then ERRORCODE is output. 8 | 9 | module bits 10 | #( 11 | parameter WIDTH = 32 12 | ) 13 | ( 14 | input wire clk, 15 | input wire reset, 16 | input wire [WIDTH-1:0] in_data, 17 | input wire in_nd, 18 | output reg [WIDTH-1:0] out_data, 19 | output reg out_nd, 20 | output reg error 21 | ); 22 | 23 | reg ready; 24 | reg [`LOG_WIDTH-1:0] bit_pos; 25 | reg pos_not_value; 26 | reg [WIDTH-1:0] stored_data; 27 | 28 | always @ (posedge clk) 29 | if (reset) 30 | begin 31 | ready <= 1'b1; 32 | error <= 1'b0; 33 | out_nd <= 1'b0; 34 | end 35 | else if (error) 36 | begin 37 | out_nd <= 1'b1; 38 | out_data <= `ERRORCODE; 39 | end 40 | else 41 | begin 42 | if (in_nd) 43 | begin 44 | if (!ready) 45 | error <= 1'b1; 46 | stored_data <= in_data; 47 | ready <= 1'b0; 48 | bit_pos <= WIDTH-1; 49 | out_nd <= 1; 50 | out_data <= WIDTH-1; 51 | pos_not_value <= 0; 52 | end 53 | else if (!ready) 54 | begin 55 | out_nd <= 1'b1; 56 | pos_not_value <= ~pos_not_value; 57 | if (pos_not_value) 58 | begin 59 | out_data <= bit_pos; 60 | end 61 | else 62 | begin 63 | out_data <= stored_data[bit_pos]; 64 | if (!(|bit_pos)) 65 | ready <= 1'b1; 66 | else 67 | bit_pos <= bit_pos - 1; 68 | end 69 | end 70 | else 71 | out_nd <= 1'b0; 72 | end 73 | 74 | endmodule -------------------------------------------------------------------------------- /verilog/uhd/dut_qa_contents.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | module dut_qa_contents; 5 | reg clk; 6 | reg rst_n; 7 | reg [`WIDTH-1:0] in_data; 8 | reg in_nd; 9 | reg [`MWIDTH-1:0] in_m; 10 | reg [`MSG_WIDTH-1:0] in_msg; 11 | reg in_msg_nd; 12 | wire [`WIDTH-1:0] out_data; 13 | wire out_nd; 14 | wire [`MWIDTH-1:0] out_m; 15 | wire [`MSG_WIDTH-1:0] out_msg; 16 | wire out_msg_nd; 17 | wire error; 18 | 19 | initial begin 20 | $from_myhdl(clk, rst_n, in_data, in_nd, in_m, in_msg, in_msg_nd); 21 | $to_myhdl(out_data, out_nd, out_m, out_msg, out_msg_nd, error); 22 | end 23 | 24 | qa_contents #(`WIDTH, `MWIDTH) qa_contents_0 25 | (.clk(clk), 26 | .rst_n(rst_n), 27 | .in_data(in_data), 28 | .in_nd(in_nd), 29 | .in_m(in_m), 30 | .in_msg(in_msg), 31 | .in_msg_nd(in_msg_nd), 32 | .out_data(out_data), 33 | .out_nd(out_nd), 34 | .out_m(out_m), 35 | .out_msg(out_msg), 36 | .out_msg_nd(out_msg_nd), 37 | .error(error) 38 | ); 39 | 40 | endmodule -------------------------------------------------------------------------------- /verilog/uhd/dut_qa_wrapper.v: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Ben Reynwar 2 | // Released under MIT License (see LICENSE.txt) 3 | 4 | // This is a wrapper around the nothing module so that it can be 5 | // accessed from the myhdl test bench. 6 | 7 | module dut_qa_wrapper; 8 | reg clk; 9 | reg reset; 10 | reg [`WIDTH-1:0] in_data; 11 | reg in_nd; 12 | wire [`WIDTH-1:0] out_data; 13 | wire out_nd; 14 | 15 | initial begin 16 | $from_myhdl(clk, reset, in_data, in_nd); 17 | $to_myhdl(out_data, out_nd); 18 | end 19 | 20 | qa_wrapper #(`WIDTH) dut 21 | (clk, reset, in_data, in_nd, out_data, out_nd); 22 | 23 | endmodule -------------------------------------------------------------------------------- /verilog/uhd/qa_wrapper.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | module qa_wrapper 6 | #( 7 | parameter WDTH = 32 8 | ) 9 | ( 10 | input wire clk, 11 | input wire reset, 12 | input wire [WDTH-1:0] in_data, 13 | input wire in_nd, 14 | output wire [WDTH-1:0] out_data, 15 | output wire out_nd 16 | ); 17 | 18 | // Separate the input stream into a sample stream and a message stream. 19 | 20 | wire [WDTH-1:0] in_samples; 21 | wire in_samples_nd; 22 | wire [`MSG_WIDTH-1:0] in_msg; 23 | wire in_msg_nd; 24 | wire splitter_error; 25 | 26 | wire rst_n; 27 | assign rst_n = ~reset; 28 | 29 | sample_msg_splitter #(WDTH) sample_msg_splitter_0 30 | (.clk(clk), 31 | .rst_n(rst_n), 32 | .in_data(in_data), 33 | .in_nd(in_nd), 34 | .out_samples(in_samples), 35 | .out_samples_nd(in_samples_nd), 36 | .out_msg(in_msg), 37 | .out_msg_nd(in_msg_nd), 38 | .error(splitter_error) 39 | ); 40 | 41 | // The qa module contains the actual blocks that we want to test. 42 | 43 | wire [WDTH-1:0] out_samples; 44 | wire out_samples_nd; 45 | wire [`MSG_WIDTH-1:0] out_msg; 46 | wire out_msg_nd; 47 | wire contents_error; 48 | wire [WDTH-1:0] in_samples_shifted; 49 | assign in_samples_shifted = {in_samples[WDTH-3:WDTH/2-1], 1'b0, in_samples[WDTH/2-2: 0], 1'b0}; 50 | 51 | // FIXME We should be able to turn off the meta info flow. 52 | reg in_m; 53 | wire out_m; 54 | 55 | qa_contents #(WDTH, 1) qa_contents_0 56 | (.clk(clk), 57 | .rst_n(rst_n), 58 | .in_data(in_samples_shifted), 59 | .in_nd(in_samples_nd), 60 | .in_m(in_m), 61 | .in_msg(in_msg), 62 | .in_msg_nd(in_msg_nd), 63 | .out_data(out_samples), 64 | .out_nd(out_samples_nd), 65 | .out_m(out_m), 66 | .out_msg(out_msg), 67 | .out_msg_nd(out_msg_nd), 68 | .error(contents_error) 69 | ); 70 | 71 | // The samples and messages are merged into common stream. 72 | 73 | wire combiner_error; 74 | wire [WDTH-1:0] out_samples_shifted; 75 | assign out_samples_shifted = {2'b0, out_samples[WDTH-1 :WDTH/2+1], out_samples[WDTH/2-1 :1]}; 76 | wire [WDTH-1:0] almostout_data; 77 | wire almostout_nd; 78 | 79 | message_stream_combiner #(2, 1, WDTH, `COMBINER_BUFFER_LENGTH, `LOG_COMBINER_BUFFER_LENGTH, `MAX_PACKET_LENGTH, `MSG_LENGTH_WIDTH) message_stream_combiner_0 80 | (.clk(clk), 81 | .rst_n(rst_n), 82 | .in_data({out_samples_shifted, out_msg}), 83 | .in_nd({out_samples_nd, out_msg_nd}), 84 | .out_data(almostout_data), 85 | .out_nd(almostout_nd), 86 | .error(combiner_error) 87 | ); 88 | 89 | wire error; 90 | assign error = splitter_error | contents_error | combiner_error; 91 | 92 | assign out_nd = (error)?1'b1:almostout_nd; 93 | assign out_data = (error)?`ERRORCODE:almostout_data; 94 | 95 | endmodule -------------------------------------------------------------------------------- /verilog/uhd/qa_wrapper_bits.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // This qa_wrapper takes the data and outputs alternately 6 | // bit position and bit contents. 7 | // If data arrives too frequently then ERRORCODE is output. 8 | 9 | module qa_wrapper 10 | #( 11 | parameter WIDTH = 32 12 | ) 13 | ( 14 | input wire clk, 15 | input wire reset, 16 | input wire [WIDTH-1:0] in_data, 17 | input wire in_nd, 18 | output wire [WIDTH-1:0] out_data, 19 | output wire out_nd 20 | ); 21 | 22 | wire error; 23 | 24 | bits #(WIDTH) bits_0 25 | (.clk(clk), 26 | .reset(reset), 27 | .in_data(in_data), 28 | .in_nd(in_nd), 29 | .out_data(out_data), 30 | .out_nd(out_nd), 31 | .error(error) 32 | ); 33 | 34 | endmodule -------------------------------------------------------------------------------- /verilog/uhd/qa_wrapper_null.v: -------------------------------------------------------------------------------- 1 | // -*- verilog -*- 2 | // Copyright (c) 2012 Ben Reynwar 3 | // Released under MIT License (see LICENSE.txt) 4 | 5 | // This qa_wrapper simple connect transmit to receive. 6 | 7 | module qa_wrapper 8 | #( 9 | parameter WDTH = 32 10 | ) 11 | ( 12 | input wire clk, 13 | input wire reset, 14 | input wire [WDTH-1:0] in_data, 15 | input wire in_nd, 16 | output wire [WDTH-1:0] out_data, 17 | output wire out_nd 18 | ); 19 | 20 | assign out_data = in_data; 21 | assign out_nd = in_nd; 22 | 23 | endmodule --------------------------------------------------------------------------------