1
0
mirror of https://github.com/pConst/basic_verilog.git synced 2025-01-28 07:02:55 +08:00

Added universal spi master module

This commit is contained in:
Konstantin Pavlov 2019-02-17 22:13:26 +03:00
parent f8794b5c48
commit f04430c7a8
6 changed files with 1135 additions and 0 deletions

333
spi_master.sv Normal file
View File

@ -0,0 +1,333 @@
//------------------------------------------------------------------------------
// spi_master.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------
// INFO ------------------------------------------------------------------------
// Universal spi master
//
// * Supports all SPI bus modes
// mode 0 (CPOL = 0, CPHA = 0)
// mode 1 (CPOL = 0, CPHA = 1)
// mode 2 (CPOL = 1, CPHA = 0)
// mode 3 (CPOL = 1, CPHA = 1)
// * Moreover, universal spi master features separate parameters to set
// clock edge to update data by spi master and
// clock edge to latch data by spi master
// * Spi clock can be made free-running (some slaves require that)
// * OE pin for bidirectional buffer connection, in case DO and DI pins are combined
//
// * Universal spi master successfully synthesize at clk speeds up to 200MHz
// * That means, that SPI clocks up to 50MHz are supported
//
/* --- INSTANTIATION TEMPLATE BEGIN ---
spi_master #(
.WRITE_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.WRITE_DATA_EDGE( 1 ),
.READ_WIDTH( 8 ),
.READ_MSB_FIRST( 1 ),
.READ_DATA_EDGE( 1 ),
.FREE_RUNNING_SPI_CLK( 0 )
) SM1 (
.clk( ),
.nrst( ),
.spi_clk( ),
.spi_wr_cmd( ),
.spi_rd_cmd( ),
.spi_busy( ),
.mosi_data( ),
.miso_data( ),
.clk_pin( ),
.ncs_pin( ),
.d_out_pin( ),
.d_oe( ),
.d_in_pin( )
);
--- INSTANTIATION TEMPLATE END ---*/
module spi_master #( parameter
bit [4:0] WRITE_WIDTH = 8, // data word width in bits
bit WRITE_MSB_FIRST = 1, // 0 - LSB first
// 1 - MSB first
bit WRITE_DATA_EDGE = 0, // 0 - master updates on rising edge
// slave reads data on falling edge
// 1 - master updates on falling edge
// slave reads data on rising edge
bit [4:0] READ_WIDTH = 8, // data word width in bits
bit READ_MSB_FIRST = 1, // 0 - LSB first
// 1 - MSB first
bit READ_DATA_EDGE = 1, // 0 - slave updates on rising edge
// master reads data on falling edge
// 1 - slave updates on falling edge
// master reads data on rising edge
bit FREE_RUNNING_SPI_CLK = 0 // 0 - clk_pin is active only when ncs_pin = 0
// 1 - clk pin is always active
)(
input clk, // system clock
input nrst, // reset (inversed)
input spi_clk, // prescaler clock
// spi_clk must be >= 4 clk cycles
// must be synchronous multiple of clk cycles
input spi_wr_cmd, // spi write command, shifting begins on rising edge
input spi_rd_cmd, // spi read command, shifting begins on rising edge
output logic spi_busy, // shifting is active
input [WRITE_WIDTH-1:0] mosi_data, // data for shifting out from master
output logic [READ_WIDTH-1:0] miso_data = 0, // shifted in data from slave
output logic clk_pin = 0, // spi master's clock pin
output logic ncs_pin = 1, // spi master's chip select (inversed)
output logic d_out_pin = 0, // spi master's data in
output logic d_oe = 1, // spi master's output enable
// in case of slave has only one SDIO pin
input d_in_pin // spi master's data out
);
// sequence_cntr[7:0]==0 - waiting for spi_wr_cmd or spi_wr_cmd
// WRITE_SEQ_START - switching mode or transaction end
// WRITE_SEQ_END - waiting for right edge to set first data
localparam WRITE_SEQ_START = 1;
localparam WRITE_SEQ_END = WRITE_SEQ_START+2*WRITE_WIDTH;
localparam READ_SEQ_START = WRITE_SEQ_END+1;
localparam READ_SEQ_END = READ_SEQ_START+2*READ_WIDTH;
logic spi_clk_rise;
logic spi_clk_fall;
edge_detect ed_spi_clk (
.clk( clk ),
.nrst( nrst ),
.in( spi_clk ),
.rising( spi_clk_rise ),
.falling( spi_clk_fall ),
.both( )
);
logic spi_wr_cmd_rise;
logic spi_rd_cmd_rise;
edge_detect ed_cmds [1:0] (
.clk( {2{clk}} ),
.nrst( {2{nrst}} ),
.in( {spi_wr_cmd,spi_rd_cmd} ),
.rising( {spi_wr_cmd_rise,spi_rd_cmd_rise} ),
.falling( ),
.both( )
);
// input synchronizer for d_in_pin in clk domain, 2 cycles delay
// making similar delay for clk edges for read operation timing
logic d_in_pin_d2;
logic spi_clk_rise_d2;
logic spi_clk_fall_d2;
delay #(
.LENGTH( 2 )
) d_in_pin_synch [2:0] (
.clk( {3{clk}} ),
.nrst( {3{nrst}} ),
.ena( {3{1'b1}} ),
.in( {d_in_pin,spi_clk_rise,spi_clk_fall} ),
.out( {d_in_pin_d2,spi_clk_rise_d2,spi_clk_fall_d2} )
);
logic [7:0] sequence_cntr = 0;
logic rd_nwr = 0; // buffering data direction
logic [WRITE_WIDTH-1:0] mosi_data_buf = 0; // buffering mosi_data
always_ff @(posedge clk) begin
if( ~nrst ) begin
miso_data[READ_WIDTH-1:0] <= 0;
clk_pin <= ~WRITE_DATA_EDGE;
ncs_pin <= 1'b1;
d_out_pin <= 1'b0;
d_oe <= 1'b1;
sequence_cntr[7:0] <= 0;
rd_nwr <= 0;
mosi_data_buf[WRITE_WIDTH-1:0] <= 0;
end else begin
if( FREE_RUNNING_SPI_CLK ) begin
if ( spi_clk_rise ) begin
clk_pin <= 1'b1;
end
if( spi_clk_fall ) begin
clk_pin <= 1'b0;
end
end else begin // FREE_RUNNING_SPI_CLK = 0
if ( ~ncs_pin &&
// fixing extra clock glitch in the end of read transaction
~((sequence_cntr[7:0] == READ_SEQ_END) &&
(WRITE_DATA_EDGE != READ_DATA_EDGE)) ) begin
if ( spi_clk_rise ) begin
clk_pin <= 1'b1;
end
if( spi_clk_fall ) begin
clk_pin <= 1'b0;
end
end else begin // ncs_pin = 1
clk_pin <= ~WRITE_DATA_EDGE;
end
end
// WRITE =======================================================================
// sequence start condition
if( sequence_cntr[7:0]==0 && (spi_wr_cmd_rise || spi_rd_cmd_rise) ) begin
// outputs should NOT be updated here
if( spi_rd_cmd_rise ) begin
rd_nwr <= 1'b1;
end else begin
rd_nwr <= 1'b0;
end
sequence_cntr[7:0] <= 1;
// buffering mosi_data to avoid data change after shift_cmd issued
mosi_data_buf[WRITE_WIDTH-1:0] <= mosi_data[WRITE_WIDTH-1:0];
end
// clocking out data
if( sequence_cntr[7:0]>=WRITE_SEQ_START && sequence_cntr[7:0]<WRITE_SEQ_END ) begin
if( WRITE_DATA_EDGE ) begin
// we should omit this edge when sequence_cntr[7:0]==WRITE_SEQ_START
if ( spi_clk_rise && (sequence_cntr[7:0]!=WRITE_SEQ_START) ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
if( spi_clk_fall ) begin
// changing d_out_pin
d_out_pin <= mosi_data_buf[WRITE_WIDTH-1];
// shifting out data
if( WRITE_MSB_FIRST ) begin
mosi_data_buf[WRITE_WIDTH-1:0] <= {mosi_data_buf[WRITE_WIDTH-2:0],1'b0};
end else begin
mosi_data_buf[WRITE_WIDTH-1:0] <= {1'b0,mosi_data_buf[WRITE_WIDTH-1:1]};
end
// spi output starts on WRITE_DATA_EDGE edge, to set first data
if( sequence_cntr[7:0]==WRITE_SEQ_START ) begin
ncs_pin <= 1'b0;
d_oe <= 1'b1;
end
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end else begin // WRITE_DATA_EDGE == 0
if ( spi_clk_rise ) begin
// changing d_out_pin
d_out_pin <= mosi_data_buf[WRITE_WIDTH-1];
// shifting out data
if( WRITE_MSB_FIRST ) begin
mosi_data_buf[WRITE_WIDTH-1:0] <= {mosi_data_buf[WRITE_WIDTH-2:0],1'b0};
end else begin
mosi_data_buf[WRITE_WIDTH-1:0] <= {1'b0,mosi_data_buf[WRITE_WIDTH-1:1]};
end
// spi output starts on WRITE_DATA_EDGE edge, to set first data
if( sequence_cntr[7:0]==WRITE_SEQ_START ) begin
ncs_pin <= 1'b0;
d_oe <= 1'b1;
end
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
// we should omit this edge when sequence_cntr[7:0]==WRITE_SEQ_START
if( spi_clk_fall && (sequence_cntr[7:0]!=WRITE_SEQ_START) ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end // if( WRITE_DATA_EDGE )
end
// waiting for valid edge to switch direction
if( ~rd_nwr ) begin
// end of write transaction
// resetting shifter to default state
if( sequence_cntr[7:0]==WRITE_SEQ_END &&
( (~WRITE_DATA_EDGE && spi_clk_rise) ||
(WRITE_DATA_EDGE && spi_clk_fall)) ) begin
ncs_pin <= 1'b1;
d_out_pin <= 1'b0;
d_oe <= 1'b1;
sequence_cntr[7:0] <= 0;
end
end else begin
if( sequence_cntr[7:0]==WRITE_SEQ_END &&
( (~WRITE_DATA_EDGE && spi_clk_rise) ||
(WRITE_DATA_EDGE && spi_clk_fall)) ) begin
//ncs_pin <= 1'b0;
d_out_pin <= 1'b0;
d_oe <= 1'b0;
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
// READ ========================================================================
// In some combinations of WRITE_DATA_EDGE and READ_DATA_EDGE and slave timing -
// we will get false first bit(s) when reading data. That is not a bug. To get valid
// read data - increment READ_WIDTH and ommit first received bit(s)
// clocking in data
// spi_clk edges and d_in_pin are 2 cycles delayed
if( sequence_cntr[7:0]>=READ_SEQ_START && sequence_cntr[7:0]<READ_SEQ_END ) begin
if( READ_DATA_EDGE ) begin
if ( spi_clk_rise_d2 && (sequence_cntr[7:0]!=READ_SEQ_START) ) begin
// shifting in delayed data
if( READ_MSB_FIRST ) begin
miso_data[READ_WIDTH-1:0] <= {miso_data[READ_WIDTH-2:0],d_in_pin_d2};
end else begin
miso_data[READ_WIDTH-1:0] <= {d_in_pin_d2,miso_data[WRITE_WIDTH-1:1]};
end
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
// we should omit this edge when sequence_cntr[7:0]==READ_SEQ_START
if( spi_clk_fall_d2 ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end else begin // READ_DATA_EDGE = 0
// we should omit this edge when sequence_cntr[7:0]==READ_SEQ_START
if( spi_clk_rise_d2 ) begin
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
if ( spi_clk_fall_d2 && (sequence_cntr[7:0]!=READ_SEQ_START) ) begin
// shifting in delayed data
if( READ_MSB_FIRST ) begin
miso_data[READ_WIDTH-1:0] <= {miso_data[READ_WIDTH-2:0],d_in_pin_d2};
end else begin
miso_data[READ_WIDTH-1:0] <= {d_in_pin_d2,miso_data[WRITE_WIDTH-1:1]};
end
sequence_cntr[7:0] <= sequence_cntr[7:0] + 1'b1;
end
end // if( READ_DATA_EDGE )
end
// waiting for valid edge to end read transaction
if( sequence_cntr[7:0]==READ_SEQ_END &&
( (~READ_DATA_EDGE && spi_clk_rise_d2) ||
(READ_DATA_EDGE && spi_clk_fall_d2)) ) begin
ncs_pin <= 1'b1;
d_out_pin <= 1'b0;
d_oe <= 1'b1;
sequence_cntr[7:0] <= 0;
end
end // if( ~rd_nwr )
end // if( nrst )
end // always
always_comb begin
spi_busy = (sequence_cntr[7:0] != 0);
end
endmodule

48
spi_master_tb/c_rand.v Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2007 Altera Corporation. All rights reserved.
// Altera products are protected under numerous U.S. and foreign patents,
// maskwork rights, copyrights and other intellectual property laws.
//
// This reference design file, and your use thereof, is subject to and governed
// by the terms and conditions of the applicable Altera Reference Design
// License Agreement (either as signed by you or found at www.altera.com). By
// using this reference design file, you indicate your acceptance of such terms
// and conditions between you and Altera Corporation. In the event that you do
// not agree with such terms and conditions, you may not use the reference
// design file and please promptly destroy any copies you have made.
//
// This reference design file is being provided on an "as-is" basis and as an
// accommodation and therefore all warranties, representations or guarantees of
// any kind (whether express, implied or statutory) including, without
// limitation, warranties of merchantability, non-infringement, or fitness for
// a particular purpose, are specifically disclaimed. By making this reference
// design file available, Altera expressly does not recommend, suggest or
// require that this reference design file be used in combination with any
// other product not provided by Altera.
/////////////////////////////////////////////////////////////////////////////
// C runtime library random number generator
//
// uses 32 logic cells for DFF/ADD and 8 DSP blocks for the
// 32x18=>32 multiply
module c_rand (clk,rst,reseed,seed_val,out);
input clk,rst,reseed;
input [31:0] seed_val;
output [15:0] out;
wire [15:0] out;
reg [31:0] state;
always @(posedge clk or posedge rst) begin
if (rst) state <= 0;
else begin
if (reseed) state <= seed_val;
else begin
state <= state * 32'h343fd + 32'h269EC3;
end
end
end
assign out = (state >> 16) & 16'h7fff;
endmodule

96
spi_master_tb/compile.tcl Normal file
View File

@ -0,0 +1,96 @@
#------------------------------------------------------------------------------
# compile.tcl
# Konstantin Pavlov, pavlovconst@gmail.com
#------------------------------------------------------------------------------
# INFO ------------------------------------------------------------------------
# Modelsim compile script
# based on "ModelSimSE general compile script version 1.1" by Doulos
# launch the script by "vsim -do compile.tcl" command on linux
# or by "modelsim.exe -do compile.tcl" on windows
# Simply change the project settings in this section
# for each new project. There should be no need to
# modify the rest of the script.
set library_file_list {
work {spi_master_tb.sv
../spi_master.sv
c_rand.v
../edge_detect.sv
../delay.sv
../clk_divider.sv}
}
set vsim_params "-L altera_mf_ver -L altera_mf -L lpm_ver -L lpm"
set top_level work.spi_master_tb
# Console commands:
# r = Recompile changed and dependent files
# rr = Recompile everything
# q = Quit without confirmation
# After sourcing the script from ModelSim for the
# first time use these commands to recompile.
proc r {} {uplevel #0 source compile.tcl}
proc rr {} {global last_compile_time
set last_compile_time 0
r }
proc q {} {quit -force }
#Does this installation support Tk?
set tk_ok 1
if [catch {package require Tk}] {set tk_ok 0}
# Prefer a fixed point font for the transcript
set PrefMain(font) {Courier 10 roman normal}
# Compile out of date files
set time_now [clock seconds]
if [catch {set last_compile_time}] {
set last_compile_time 0
}
foreach {library file_list} $library_file_list {
vlib $library
vmap work $library
foreach file $file_list {
if { $last_compile_time < [file mtime $file] } {
if [regexp {.vhdl?$} $file] {
vcom -93 $file
} else {
vlog $file
}
set last_compile_time 0
}
}
}
set last_compile_time $time_now
# Load the simulation
eval vsim $top_level $vsim_params
# Load saved wave patterns
do wave.do
# Run the simulation
run 100us
wave zoom range 0 100us
# How long since project began?
if {[file isfile start_time.txt] == 0} {
set f [open start_time.txt w]
puts $f "Start time was [clock seconds]"
close $f
} else {
set f [open start_time.txt r]
set line [gets $f]
close $f
regexp {\d+} $line start_time
set total_time [expr ([clock seconds]-$start_time)/60]
puts "Project time is $total_time minutes"
}

324
spi_master_tb/modelsim.ini Normal file
View File

@ -0,0 +1,324 @@
; Copyright 1991-2009 Mentor Graphics Corporation
;
; All Rights Reserved.
;
; THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS THE PROPERTY OF
; MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS SUBJECT TO LICENSE TERMS.
;
[Library]
others = $MODEL_TECH/../modelsim.ini
; Altera Primitive libraries
;
; VHDL Section
;
;
; Verilog Section
;
work = work
[vcom]
; VHDL93 variable selects language version as the default.
; Default is VHDL-2002.
; Value of 0 or 1987 for VHDL-1987.
; Value of 1 or 1993 for VHDL-1993.
; Default or value of 2 or 2002 for VHDL-2002.
; Default or value of 3 or 2008 for VHDL-2008.
VHDL93 = 2002
; Show source line containing error. Default is off.
; Show_source = 1
; Turn off unbound-component warnings. Default is on.
; Show_Warning1 = 0
; Turn off process-without-a-wait-statement warnings. Default is on.
; Show_Warning2 = 0
; Turn off null-range warnings. Default is on.
; Show_Warning3 = 0
; Turn off no-space-in-time-literal warnings. Default is on.
; Show_Warning4 = 0
; Turn off multiple-drivers-on-unresolved-signal warnings. Default is on.
; Show_Warning5 = 0
; Turn off optimization for IEEE std_logic_1164 package. Default is on.
; Optimize_1164 = 0
; Turn on resolving of ambiguous function overloading in favor of the
; "explicit" function declaration (not the one automatically created by
; the compiler for each type declaration). Default is off.
; The .ini file has Explicit enabled so that std_logic_signed/unsigned
; will match the behavior of synthesis tools.
Explicit = 1
; Turn off acceleration of the VITAL packages. Default is to accelerate.
; NoVital = 1
; Turn off VITAL compliance checking. Default is checking on.
; NoVitalCheck = 1
; Ignore VITAL compliance checking errors. Default is to not ignore.
; IgnoreVitalErrors = 1
; Turn off VITAL compliance checking warnings. Default is to show warnings.
; Show_VitalChecksWarnings = 0
; Keep silent about case statement static warnings.
; Default is to give a warning.
; NoCaseStaticError = 1
; Keep silent about warnings caused by aggregates that are not locally static.
; Default is to give a warning.
; NoOthersStaticError = 1
; Turn off inclusion of debugging info within design units.
; Default is to include debugging info.
; NoDebug = 1
; Turn off "Loading..." messages. Default is messages on.
; Quiet = 1
; Turn on some limited synthesis rule compliance checking. Checks only:
; -- signals used (read) by a process must be in the sensitivity list
; CheckSynthesis = 1
; Activate optimizations on expressions that do not involve signals,
; waits, or function/procedure/task invocations. Default is off.
; ScalarOpts = 1
; Require the user to specify a configuration for all bindings,
; and do not generate a compile time default binding for the
; component. This will result in an elaboration error of
; 'component not bound' if the user fails to do so. Avoids the rare
; issue of a false dependency upon the unused default binding.
; RequireConfigForAllDefaultBinding = 1
; Inhibit range checking on subscripts of arrays. Range checking on
; scalars defined with subtypes is inhibited by default.
; NoIndexCheck = 1
; Inhibit range checks on all (implicit and explicit) assignments to
; scalar objects defined with subtypes.
; NoRangeCheck = 1
[vlog]
; Turn off inclusion of debugging info within design units.
; Default is to include debugging info.
; NoDebug = 1
; Turn off "loading..." messages. Default is messages on.
; Quiet = 1
; Turn on Verilog hazard checking (order-dependent accessing of global vars).
; Default is off.
; Hazard = 1
; Turn on converting regular Verilog identifiers to uppercase. Allows case
; insensitivity for module names. Default is no conversion.
; UpCase = 1
; Turn on incremental compilation of modules. Default is off.
; Incremental = 1
; Turns on lint-style checking.
; Show_Lint = 1
[vsim]
; Simulator resolution
; Set to fs, ps, ns, us, ms, or sec with optional prefix of 1, 10, or 100.
Resolution = ps
; User time unit for run commands
; Set to default, fs, ps, ns, us, ms, or sec. The default is to use the
; unit specified for Resolution. For example, if Resolution is 100ps,
; then UserTimeUnit defaults to ps.
; Should generally be set to default.
UserTimeUnit = default
; Default run length
RunLength = 100
; Maximum iterations that can be run without advancing simulation time
IterationLimit = 5000
; Directive to license manager:
; vhdl Immediately reserve a VHDL license
; vlog Immediately reserve a Verilog license
; plus Immediately reserve a VHDL and Verilog license
; nomgc Do not look for Mentor Graphics Licenses
; nomti Do not look for Model Technology Licenses
; noqueue Do not wait in the license queue when a license isn't available
; viewsim Try for viewer license but accept simulator license(s) instead
; of queuing for viewer license
; License = plus
; Stop the simulator after a VHDL/Verilog assertion message
; 0 = Note 1 = Warning 2 = Error 3 = Failure 4 = Fatal
BreakOnAssertion = 3
; Assertion Message Format
; %S - Severity Level
; %R - Report Message
; %T - Time of assertion
; %D - Delta
; %I - Instance or Region pathname (if available)
; %% - print '%' character
; AssertionFormat = "** %S: %R\n Time: %T Iteration: %D%I\n"
; Assertion File - alternate file for storing VHDL/Verilog assertion messages
; AssertFile = assert.log
; Default radix for all windows and commands...
; Set to symbolic, ascii, binary, octal, decimal, hex, unsigned
DefaultRadix = symbolic
; VSIM Startup command
; Startup = do startup.do
; File for saving command transcript
TranscriptFile = transcript
; File for saving command history
; CommandHistory = cmdhist.log
; Specify whether paths in simulator commands should be described
; in VHDL or Verilog format.
; For VHDL, PathSeparator = /
; For Verilog, PathSeparator = .
; Must not be the same character as DatasetSeparator.
PathSeparator = /
; Specify the dataset separator for fully rooted contexts.
; The default is ':'. For example, sim:/top
; Must not be the same character as PathSeparator.
DatasetSeparator = :
; Disable VHDL assertion messages
; IgnoreNote = 1
; IgnoreWarning = 1
; IgnoreError = 1
; IgnoreFailure = 1
; Default force kind. May be freeze, drive, deposit, or default
; or in other terms, fixed, wired, or charged.
; A value of "default" will use the signal kind to determine the
; force kind, drive for resolved signals, freeze for unresolved signals
; DefaultForceKind = freeze
; If zero, open files when elaborated; otherwise, open files on
; first read or write. Default is 0.
; DelayFileOpen = 1
; Control VHDL files opened for write.
; 0 = Buffered, 1 = Unbuffered
UnbufferedOutput = 0
; Control the number of VHDL files open concurrently.
; This number should always be less than the current ulimit
; setting for max file descriptors.
; 0 = unlimited
ConcurrentFileLimit = 40
; Control the number of hierarchical regions displayed as
; part of a signal name shown in the Wave window.
; A value of zero tells VSIM to display the full name.
; The default is 0.
; WaveSignalNameWidth = 0
; Turn off warnings from the std_logic_arith, std_logic_unsigned
; and std_logic_signed packages.
; StdArithNoWarnings = 1
; Turn off warnings from the IEEE numeric_std and numeric_bit packages.
; NumericStdNoWarnings = 1
; Control the format of the (VHDL) FOR generate statement label
; for each iteration. Do not quote it.
; The format string here must contain the conversion codes %s and %d,
; in that order, and no other conversion codes. The %s represents
; the generate_label; the %d represents the generate parameter value
; at a particular generate iteration (this is the position number if
; the generate parameter is of an enumeration type). Embedded whitespace
; is allowed (but discouraged); leading and trailing whitespace is ignored.
; Application of the format must result in a unique scope name over all
; such names in the design so that name lookup can function properly.
; GenerateFormat = %s__%d
; Specify whether checkpoint files should be compressed.
; The default is 1 (compressed).
; CheckpointCompressMode = 0
; List of dynamically loaded objects for Verilog PLI applications
; Veriuser = veriuser.sl
; Specify default options for the restart command. Options can be one
; or more of: -force -nobreakpoint -nolist -nolog -nowave
; DefaultRestartOptions = -force
; HP-UX 10.20 ONLY - Enable memory locking to speed up large designs
; (> 500 megabyte memory footprint). Default is disabled.
; Specify number of megabytes to lock.
; LockedMemory = 1000
; Turn on (1) or off (0) WLF file compression.
; The default is 1 (compress WLF file).
; WLFCompress = 0
; Specify whether to save all design hierarchy (1) in the WLF file
; or only regions containing logged signals (0).
; The default is 0 (save only regions with logged signals).
; WLFSaveAllRegions = 1
; WLF file time limit. Limit WLF file by time, as closely as possible,
; to the specified amount of simulation time. When the limit is exceeded
; the earliest times get truncated from the file.
; If both time and size limits are specified the most restrictive is used.
; UserTimeUnits are used if time units are not specified.
; The default is 0 (no limit). Example: WLFTimeLimit = {100 ms}
; WLFTimeLimit = 0
; WLF file size limit. Limit WLF file size, as closely as possible,
; to the specified number of megabytes. If both time and size limits
; are specified then the most restrictive is used.
; The default is 0 (no limit).
; WLFSizeLimit = 1000
; Specify whether or not a WLF file should be deleted when the
; simulation ends. A value of 1 will cause the WLF file to be deleted.
; The default is 0 (do not delete WLF file when simulation ends).
; WLFDeleteOnQuit = 1
; Automatic SDF compilation
; Disables automatic compilation of SDF files in flows that support it.
; Default is on, uncomment to turn off.
; NoAutoSDFCompile = 1
[lmc]
[msg_system]
; Change a message severity or suppress a message.
; The format is: <msg directive> = <msg number>[,<msg number>...]
; Examples:
; note = 3009
; warning = 3033
; error = 3010,3016
; fatal = 3016,3033
; suppress = 3009,3016,3043
; The command verror <msg number> can be used to get the complete
; description of a message.
; Control transcripting of elaboration/runtime messages.
; The default is to have messages appear in the transcript and
; recorded in the wlf file (messages that are recorded in the
; wlf file can be viewed in the MsgViewer). The other settings
; are to send messages only to the transcript or only to the
; wlf file. The valid values are
; both {default}
; tran {transcript only}
; wlf {wlf file only}
; msgmode = both

View File

@ -0,0 +1,273 @@
//------------------------------------------------------------------------------
// spi_master_tb.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------
// INFO ------------------------------------------------------------------------
//
`timescale 1ns / 1ps
module spi_master_tb();
logic clk200;
initial begin
#0 clk200 = 1'b0;
forever
#2.5 clk200 = ~clk200;
end
// external device "asynchronous" clock
logic clk33;
initial begin
#0 clk33 = 1'b0;
forever
#15.151 clk33 = ~clk33;
end
logic rst;
initial begin
#0 rst = 1'b0;
#10.2 rst = 1'b1;
#5 rst = 1'b0;
//#10000;
forever begin
#9985 rst = ~rst;
#5 rst = ~rst;
end
end
logic nrst;
assign nrst = ~rst;
logic rst_once;
initial begin
#0 rst_once = 1'b0;
#10.2 rst_once = 1'b1;
#5 rst_once = 1'b0;
end
logic nrst_once;
assign nrst_once = ~rst_once;
logic [31:0] DerivedClocks;
clk_divider #(
.WIDTH( 32 )
) cd1 (
.clk( clk200 ),
.nrst( nrst_once ),
.ena( 1'b1 ),
.out( DerivedClocks[31:0] )
);
logic [31:0] E_DerivedClocks;
edge_detect ed1[31:0] (
.clk( {32{clk200}} ),
.nrst( {32{nrst_once}} ),
.in( DerivedClocks[31:0] ),
.rising( E_DerivedClocks[31:0] ),
.falling( ),
.both( )
);
logic [15:0] RandomNumber1;
c_rand rng1 (
.clk( clk200 ),
.rst( rst_once ),
.reseed( 1'b0 ),
.seed_val( DerivedClocks[31:0] ),
.out( RandomNumber1[15:0] )
);
logic start;
initial begin
#0 start = 1'b0;
#100 start = 1'b1;
#20 start = 1'b0;
end
// Module under test ==========================================================
logic oe1_pin, din1_pin, clk1_pin, clk1_pin_rise, clk1_pin_fall;
logic oe2_pin, din2_pin, clk2_pin, clk2_pin_rise, clk2_pin_fall;
logic oe3_pin, din3_pin, clk3_pin, clk3_pin_rise, clk3_pin_fall;
logic oe4_pin, din4_pin, clk4_pin, clk4_pin_rise, clk4_pin_fall;
edge_detect ed2[3:0] (
.clk( {4{clk200}} ),
.nrst( {4{1'b1}} ),
.in( {clk1_pin, clk2_pin, clk3_pin, clk4_pin} ),
.rising( {clk1_pin_rise, clk2_pin_rise, clk3_pin_rise, clk4_pin_rise} ),
.falling( {clk1_pin_fall, clk2_pin_fall, clk3_pin_fall, clk4_pin_fall} ),
.both( )
);
reg [7:0] test1_data = 8'b1010_0011;
reg [7:0] test2_data = 8'b1010_0011;
reg [7:0] test3_data = 8'b1010_0011;
reg [7:0] test4_data = 8'b1010_0011;
spi_master #(
.WRITE_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.WRITE_DATA_EDGE( 1 ),
.READ_WIDTH( 8 ),
.READ_MSB_FIRST( 1 ),
.READ_DATA_EDGE( 1 ),
.FREE_RUNNING_SPI_CLK( 0 )
) SM1 (
.clk( clk200 ),
.nrst( nrst_once ),
.spi_clk( DerivedClocks[1] ),
.spi_wr_cmd( 0 ),
.spi_rd_cmd( start ),
.spi_busy( ),
.mosi_data( 8'b1010_0011 ),
.miso_data( ),
.clk_pin( clk1_pin ),
.ncs_pin( ),
.d_out_pin( ),
.d_oe( oe1_pin ),
.d_in_pin( din1_pin )
);
spi_master #(
.WRITE_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.WRITE_DATA_EDGE( 0 ),
.READ_WIDTH( 8 ),
.READ_MSB_FIRST( 1 ),
.READ_DATA_EDGE( 0 ),
.FREE_RUNNING_SPI_CLK( 0 )
) SM2 (
.clk( clk200 ),
.nrst( nrst_once ),
.spi_clk( DerivedClocks[1] ),
.spi_wr_cmd( 0 ),
.spi_rd_cmd( start ),
.spi_busy( ),
.mosi_data( 8'b1010_0011 ),
.miso_data( ),
.clk_pin( clk2_pin ),
.ncs_pin( ),
.d_out_pin( ),
.d_oe( oe2_pin ),
.d_in_pin( din2_pin )
);
spi_master #(
.WRITE_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.WRITE_DATA_EDGE( 1 ),
.READ_WIDTH( 8 ),
.READ_MSB_FIRST( 1 ),
.READ_DATA_EDGE( 0 ),
.FREE_RUNNING_SPI_CLK( 0 )
) SM3 (
.clk( clk200 ),
.nrst( nrst_once ),
.spi_clk( DerivedClocks[1] ),
.spi_wr_cmd( 0 ),
.spi_rd_cmd( start ),
.spi_busy( ),
.mosi_data( 8'b1010_0011 ),
.miso_data( ),
.clk_pin( clk3_pin ),
.ncs_pin( ),
.d_out_pin( ),
.d_oe( oe3_pin ),
.d_in_pin( din3_pin )
);
spi_master #(
.WRITE_WIDTH( 8 ),
.WRITE_MSB_FIRST( 1 ),
.WRITE_DATA_EDGE( 0 ),
.READ_WIDTH( 8 ),
.READ_MSB_FIRST( 1 ),
.READ_DATA_EDGE( 1 ),
.FREE_RUNNING_SPI_CLK( 0 )
) SM4 (
.clk( clk200 ),
.nrst( nrst_once ),
.spi_clk( DerivedClocks[1] ),
.spi_wr_cmd( 0 ),
.spi_rd_cmd( start ),
.spi_busy( ),
.mosi_data( 8'b1010_0011 ),
.miso_data( ),
.clk_pin( clk4_pin ),
.ncs_pin( ),
.d_out_pin( ),
.d_oe( oe4_pin ),
.d_in_pin( din4_pin )
);
// emulating external divice ==================================================
// that works asynchronously on clk33 clock
always_ff @(posedge clk200) begin
if( ~nrst_once) begin
din1_pin <= 0;
test1_data[7:0] = 8'b1010_0011;
end else begin
if( ~oe1_pin && clk1_pin_rise ) begin
din1_pin <=test1_data[7];
test1_data[7:0] <= {test1_data[6:0],1'b0};
end
end
end
always_ff @(posedge clk200) begin
if( ~nrst_once) begin
din2_pin <= 0;
test2_data[7:0] = 8'b1010_0011;
end else begin
if( ~oe2_pin && clk2_pin_fall ) begin
din2_pin <=test2_data[7];
test2_data[7:0] <= {test2_data[6:0],1'b0};
end
end
end
always_ff @(posedge clk200) begin
if( ~nrst_once) begin
din3_pin <= 0;
test3_data[7:0] = 8'b1010_0011;
end else begin
if( ~oe3_pin && clk3_pin_fall ) begin
din3_pin <=test3_data[7];
test3_data[7:0] <= {test3_data[6:0],1'b0};
end
end
end
always_ff @(posedge clk200) begin
if( ~nrst_once) begin
din4_pin <= 0;
test4_data[7:0] = 8'b1010_0011;
end else begin
if( ~oe4_pin && clk4_pin_fall ) begin
din4_pin <=test4_data[7];
test4_data[7:0] <= {test4_data[6:0],1'b0};
end
end
end
endmodule

61
spi_master_tb/wave.do Normal file
View File

@ -0,0 +1,61 @@
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /spi_master_tb/SM1/clk
add wave -noupdate /spi_master_tb/SM1/nrst
add wave -noupdate /spi_master_tb/SM1/spi_clk
add wave -noupdate /spi_master_tb/SM1/spi_clk_rise
add wave -noupdate /spi_master_tb/SM1/spi_clk_fall
add wave -noupdate /spi_master_tb/SM1/spi_wr_cmd_rise
add wave -noupdate /spi_master_tb/SM1/spi_wr_cmd
add wave -noupdate /spi_master_tb/SM1/spi_rd_cmd
add wave -noupdate /spi_master_tb/SM1/spi_rd_cmd_rise
add wave -noupdate /spi_master_tb/SM1/spi_busy
add wave -noupdate -radix decimal /spi_master_tb/SM1/sequence_cntr
add wave -noupdate /spi_master_tb/SM1/rd_nwr
add wave -noupdate -radix binary /spi_master_tb/SM1/data_out
add wave -noupdate -radix binary /spi_master_tb/SM1/data_in
add wave -noupdate /spi_master_tb/SM1/data_out_buf
add wave -noupdate -color Yellow /spi_master_tb/SM1/clk_pin
add wave -noupdate -color Yellow /spi_master_tb/SM1/ncs_pin
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_out_pin
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_oe
add wave -noupdate -color Yellow /spi_master_tb/SM1/d_in_pin
add wave -noupdate /spi_master_tb/SM1/spi_clk_rise_d2
add wave -noupdate /spi_master_tb/SM1/spi_clk_fall_d2
add wave -noupdate /spi_master_tb/SM1/d_in_pin_d2
add wave -noupdate -color {Medium Violet Red} -radix decimal /spi_master_tb/SM2/sequence_cntr
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/clk_pin
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/ncs_pin
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_out_pin
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_oe
add wave -noupdate -color {Medium Violet Red} /spi_master_tb/SM2/d_in_pin
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/sequence_cntr
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/clk_pin
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/ncs_pin
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_out_pin
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_oe
add wave -noupdate -color {Cornflower Blue} /spi_master_tb/SM3/d_in_pin
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/sequence_cntr
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/clk_pin
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/ncs_pin
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_out_pin
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_oe
add wave -noupdate -color {Orange Red} /spi_master_tb/SM4/d_in_pin
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {801886 ps} 0}
quietly wave cursor active 1
configure wave -namecolwidth 150
configure wave -valuecolwidth 100
configure wave -justifyvalue right
configure wave -signalnamewidth 0
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {0 ps} {3706084 ps}