mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
Merge branch 'main' of https://github.com/openhwgroup/cvw into rvvi_setup
This commit is contained in:
commit
571ec51658
2
.github/workflows/install.yml
vendored
2
.github/workflows/install.yml
vendored
@ -18,7 +18,7 @@ on:
|
||||
paths:
|
||||
- 'bin/wally-tool-chain-install.sh'
|
||||
- 'bin/wally-distro-check.sh'
|
||||
- 'wally-package-install.sh'
|
||||
- 'bin/wally-package-install.sh'
|
||||
schedule:
|
||||
- cron: "0 7 * * 3" # Run at 12:00 AM Pacific Time on Wednesdays
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ The Synopsys Installer automatically installs all downloaded product files into
|
||||
```
|
||||
|
||||
> [!Note]
|
||||
> Although most parts of Wally, including the Questa simulator, will work on most modern Linux platforms, as of 2022, the Synopsys CAD tools for SoC design are only supported on RedHat Enterprise Linux 7.4 or 8 or SUSE Linux Enterprise Server (SLES) 12 or 15. Moreover, the RISC-V formal specification (sail-riscv) does not build gracefully on RHEL7.
|
||||
> Although most parts of Wally, including the Questa simulator, will work on most modern Linux platforms, as of 2024, the Synopsys CAD tools for SoC design are only supported on Red Hat Enterprise Linux (or AlmaLinux/Rocky) 8.4+ or 9.1+ or SUSE Linux Enterprise Server (SLES) 15.
|
||||
|
||||
The Verilog simulation has been tested with Siemens Questa/ModelSim. This package is available to universities worldwide as part of the Design Verification Bundle through the Siemens Academic Partner Program members for $990/year.
|
||||
|
||||
|
||||
@ -288,33 +288,34 @@ class TestRunner:
|
||||
makefile_location = self.cvw.joinpath(makefile_path)
|
||||
os.chdir(makefile_location)
|
||||
|
||||
output_file = self.log_dir.joinpath(f"make-{target}-output.log")
|
||||
if target:
|
||||
output_file = self.log_dir.joinpath(f"make-{target}-output.log")
|
||||
else: output_file = self.log_dir.joinpath(f"make-output.log")
|
||||
|
||||
command = ["make"]
|
||||
|
||||
# Add target to the command if specified
|
||||
if target:
|
||||
command.append(target)
|
||||
self.logger.info(f"Command used in directory {makefile_location}: {command[0]} {command[1]}")
|
||||
# Execute make with target and cores/2
|
||||
if target:
|
||||
command = ["make", target, "--jobs=$(($(nproc)/2))"]
|
||||
else:
|
||||
self.logger.info(f"Command used in directory {makefile_location}: {command[0]}")
|
||||
command = ["make", "--jobs=$(($(nproc)/2))"]
|
||||
|
||||
self.logger.info(f"Command used in directory {makefile_location}: {' '.join(command)}")
|
||||
|
||||
# Execute the command using subprocess and save the output into a file
|
||||
with open(output_file, "w") as f:
|
||||
formatted_datetime = self.current_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
f.write(formatted_datetime)
|
||||
f.write("\n\n")
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True)
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True, shell=True)
|
||||
|
||||
# Execute the command using a subprocess and not save the output
|
||||
#result = subprocess.run(command, text=True)
|
||||
|
||||
# Check the result
|
||||
if result.returncode == 0:
|
||||
self.logger.info(f"Tests have been made with target: {target}")
|
||||
self.logger.info(f"Tests have been made with target: {' '.join(command)}")
|
||||
return True
|
||||
else:
|
||||
self.logger.error(f"Error making the tests. Target: {target}")
|
||||
self.logger.error(f"Error making the tests. Command: {' '.join(command)}")
|
||||
return False
|
||||
|
||||
def run_tests(self, test_type=None, test_name=None, test_extensions=None):
|
||||
@ -422,7 +423,7 @@ class TestRunner:
|
||||
elif "Failures detected in output" in line: # Text explicitly fails
|
||||
try:
|
||||
config_name = line.split(':')[0].strip()
|
||||
log_file = os.path.abspath(os.path.join("logs", config_name, ".log"))
|
||||
log_file = os.path.abspath(os.path.join("logs", config_name))
|
||||
failed_configs.append((config_name, log_file))
|
||||
except:
|
||||
failed_configs.append((config_name, "Log file not found"))
|
||||
@ -672,7 +673,7 @@ def main():
|
||||
|
||||
parser.add_argument('--path',default = "nightly", help='specify the path for where the nightly repositories will be cloned ex: "nightly-runs')
|
||||
parser.add_argument('--repository',default = "https://github.com/openhwgroup/cvw", help='specify which github repository you want to clone')
|
||||
parser.add_argument('--target', default = "--jobs", help='types of tests you can make are: all, wally-riscv-arch-test, no')
|
||||
parser.add_argument('--target', default = "", help='types of tests you can make are: all, wally-riscv-arch-test, no')
|
||||
parser.add_argument('--tests', default = "nightly", help='types of tests you can run are: nightly, test, test_lint')
|
||||
parser.add_argument('--send_email',default = "", nargs="+", help='What emails to send test results to. Example: "[email1],[email2],..."')
|
||||
|
||||
|
||||
@ -334,22 +334,29 @@ def search_log_for_text(text, grepfile):
|
||||
# print(" search_log_for_text invoking %s" % grepcmd)
|
||||
return os.system(grepcmd) == 0
|
||||
|
||||
def run_test_case(config):
|
||||
"""Run the given test case, and return 0 if the test suceeds and 1 if it fails"""
|
||||
def run_test_case(config, dryrun: bool = False):
|
||||
"""
|
||||
Run the given test case, and return 0 if the test suceeds and 1 if it fails
|
||||
|
||||
Do not execute commands if dryrun
|
||||
"""
|
||||
grepfile = config.grepfile
|
||||
cmd = config.cmd
|
||||
os.chdir(regressionDir)
|
||||
# print(" run_test_case invoking %s" % cmd, flush=True)
|
||||
os.system(cmd)
|
||||
if search_log_for_text(config.grepstr, grepfile):
|
||||
# Flush is needed to flush output to stdout when running in multiprocessing Pool
|
||||
# print(f"{bcolors.OKGREEN}%s_%s: Success{bcolors.ENDC}" % (config.variant, config.name), flush=True)
|
||||
print(f"{bcolors.OKGREEN}%s: Success{bcolors.ENDC}" % (config.cmd), flush=True)
|
||||
if dryrun:
|
||||
print(f"Executing {cmd}", flush=True)
|
||||
return 0
|
||||
else:
|
||||
print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (config.cmd), flush=True)
|
||||
print(" Check %s" % grepfile)
|
||||
return 1
|
||||
os.system(cmd)
|
||||
if search_log_for_text(config.grepstr, grepfile):
|
||||
# Flush is needed to flush output to stdout when running in multiprocessing Pool
|
||||
# print(f"{bcolors.OKGREEN}%s_%s: Success{bcolors.ENDC}" % (config.variant, config.name), flush=True)
|
||||
print(f"{bcolors.OKGREEN}%s: Success{bcolors.ENDC}" % (config.cmd), flush=True)
|
||||
return 0
|
||||
else:
|
||||
print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (config.cmd), flush=True)
|
||||
print(" Check %s" % grepfile)
|
||||
return 1
|
||||
|
||||
##################################
|
||||
# Main body
|
||||
@ -363,6 +370,7 @@ os.chdir(regressionDir)
|
||||
coveragesim = "questa" # Questa is required for code/functional coverage
|
||||
#defaultsim = "questa" # Default simulator for all other tests; change to Verilator when flow is ready
|
||||
defaultsim = "verilator" # Default simulator for all other tests
|
||||
lockstepsim = "questa"
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--ccov", help="Code Coverage", action="store_true")
|
||||
@ -371,6 +379,7 @@ parser.add_argument("--nightly", help="Run large nightly regression", action="st
|
||||
parser.add_argument("--buildroot", help="Include Buildroot Linux boot test (takes many hours, done along with --nightly)", action="store_true")
|
||||
parser.add_argument("--testfloat", help="Include Testfloat floating-point unit tests", action="store_true")
|
||||
parser.add_argument("--fp", help="Include floating-point tests in coverage (slower runtime)", action="store_true")
|
||||
parser.add_argument("--dryrun", help="Print commands invoked to console without running regression", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if (args.nightly):
|
||||
@ -404,7 +413,7 @@ configs = [
|
||||
# run full buildroot boot simulation (slow) if buildroot flag is set. Start it early to overlap with other tests
|
||||
if (args.buildroot):
|
||||
# addTests(tests_buildrootboot, defaultsim) # non-lockstep with Verilator runs in about 2 hours
|
||||
addTests(tests_buildrootbootlockstep, "questa") # lockstep with Questa and ImperasDV runs overnight
|
||||
addTests(tests_buildrootbootlockstep, lockstepsim) # lockstep with Questa and ImperasDV runs overnight
|
||||
|
||||
if (args.ccov): # only run RV64GC tests on Questa in code coverage mode
|
||||
addTests(tests64gc_nofp, coveragesim)
|
||||
@ -417,7 +426,7 @@ elif (args.fcov): # only run RV64GC tests on Questa in lockstep in functional c
|
||||
|
||||
else:
|
||||
for sim in sims:
|
||||
if (not (args.buildroot and sim == defaultsim)): # skip short buildroot sim if running long one
|
||||
if (not (args.buildroot and sim == lockstepsim)): # skip short buildroot sim if running long one
|
||||
addTests(tests_buildrootshort, sim)
|
||||
addTests(tests, sim)
|
||||
addTests(tests64gc_nofp, sim)
|
||||
@ -425,9 +434,10 @@ else:
|
||||
|
||||
# run derivative configurations and lockstep tests in nightly regression
|
||||
if (args.nightly):
|
||||
addLockstepTestsByDir(WALLY+"/tests/coverage", "rv64gc", "questa", 0)
|
||||
addLockstepTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv64i_m", "rv64gc", "questa", 0)
|
||||
addLockstepTestsByDir(WALLY+"/tests/coverage", "rv64gc", lockstepsim, 0)
|
||||
addLockstepTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv64i_m", "rv64gc", lockstepsim, 0)
|
||||
addTests(derivconfigtests, defaultsim)
|
||||
# addTests(bpredtests, defaultsim) # This is currently broken in regression due to something related to the new wsim script.
|
||||
|
||||
# testfloat tests
|
||||
if (args.testfloat): # for testfloat alone, just run testfloat tests
|
||||
@ -540,9 +550,9 @@ def main():
|
||||
|
||||
# Coverage report
|
||||
if args.ccov:
|
||||
os.system('make QuestaCodeCoverage')
|
||||
os.system('make QuestaCodeCoverage')
|
||||
if args.fcov:
|
||||
os.system('make -f '+WALLY+'/addins/cvw-arch-verif/Makefile merge')
|
||||
os.system('make -f '+WALLY+'/addins/cvw-arch-verif/Makefile merge')
|
||||
# Count the number of failures
|
||||
if num_fail:
|
||||
print(f"{bcolors.FAIL}Regression failed with %s failed configurations{bcolors.ENDC}" % num_fail)
|
||||
|
||||
@ -123,5 +123,14 @@ else
|
||||
eval "$UPDATE_COMMAND"
|
||||
# Install packages listed above using appropriate package manager
|
||||
sudo $PACKAGE_MANAGER install -y "${GENERAL_PACKAGES[@]}" "${GNU_PACKAGES[@]}" "${QEMU_PACKAGES[@]}" "${SPIKE_PACKAGES[@]}" "${VERILATOR_PACKAGES[@]}" "${BUILDROOT_PACKAGES[@]}" "${OTHER_PACKAGES[@]}" "${VIVADO_PACKAGES[@]}"
|
||||
|
||||
# Post install steps
|
||||
# Vivado looks for ncurses5 libraries, but Ubuntu 24.04 only has ncurses6
|
||||
# Create symbolic links to the ncurses6 libraries to fool Vivado
|
||||
if (( UBUNTU_VERSION >= 24 )); then
|
||||
sudo ln -vsf /lib/x86_64-linux-gnu/libncurses.so.6 /lib/x86_64-linux-gnu/libncurses.so.5
|
||||
sudo ln -vsf /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libntinfo.so.5
|
||||
fi
|
||||
|
||||
echo -e "${SUCCESS_COLOR}Packages successfully installed.${ENDC}"
|
||||
fi
|
||||
|
||||
13
bin/wsim
13
bin/wsim
@ -59,12 +59,12 @@ if(args.testsuite.endswith('.elf') and args.elf == ""): # No --elf argument; che
|
||||
args.testsuite = fields[1] + "_" + fields[3]
|
||||
else:
|
||||
args.testsuite = fields[2] + "_" + fields[3]
|
||||
elif ('/' in args.testsuite):
|
||||
elif ('/' in args.testsuite):
|
||||
args.testsuite=args.testsuite.rsplit('/', 1)[1] # strip off path if present
|
||||
else:
|
||||
print("ELF file not found: " + args.testsuite)
|
||||
exit(1)
|
||||
|
||||
|
||||
if(args.lockstep and not args.testsuite.endswith('.elf')):
|
||||
print(f"Invalid Options. Cannot run a testsuite, {args.testsuite} with lockstep. Must run a single elf.")
|
||||
exit(1)
|
||||
@ -89,13 +89,14 @@ if(int(args.locksteplog) >= 1): EnableLog = 1
|
||||
else: EnableLog = 0
|
||||
prefix = ""
|
||||
if (args.lockstep or args.lockstepverbose or args.fcov or args.fcovimp):
|
||||
if (args.sim == "questa" or args.sim == "vcs"):
|
||||
if (args.sim == "questa" or args.sim == "vcs"):
|
||||
prefix = "IMPERAS_TOOLS=" + WALLY + "/config/"+args.config+"/imperas.ic"
|
||||
if (args.sim == "questa"):
|
||||
prefix = "MTI_VCO_MODE=64 " + prefix
|
||||
# Force Questa to use 64-bit mode, sometimes it defaults to 32-bit even on 64-bit machines
|
||||
if (args.sim == "questa"):
|
||||
prefix = "MTI_VCO_MODE=64 " + prefix
|
||||
|
||||
if (args.lockstep or args.lockstepverbose):
|
||||
if(args.locksteplog != 0): ImperasPlusArgs = " +IDV_TRACE2LOG=" + str(EnableLog) + " +IDV_TRACE2LOG_AFTER=" + str(args.locksteplog)
|
||||
if(args.locksteplog != 0): ImperasPlusArgs = " +IDV_TRACE2LOG=" + str(EnableLog) + " +IDV_TRACE2LOG_AFTER=" + str(args.locksteplog)
|
||||
else: ImperasPlusArgs = ""
|
||||
if(args.fcovimp):
|
||||
CovEnableStr = "1" if int(args.covlog) > 0 else "0"
|
||||
|
||||
@ -76,7 +76,7 @@ module fround import cvw::*; #(parameter cvw_t P) (
|
||||
assign Eeqm1 = ($signed(E) == -1);
|
||||
|
||||
// Logic for nonnegative mask and rounding bits
|
||||
assign IMask = {1'b1, {P.NF{1'b0}}} >>> E; /// if E > Nf, this produces all 0s instead of all 1s. Hence exact handling is needed below.
|
||||
assign IMask = $signed({1'b1, {P.NF{1'b0}}}) >>> E; /// if E > Nf, this produces all 0s instead of all 1s. Hence exact handling is needed below.
|
||||
assign Tmasknonneg = ~IMask >>> 1'b1;
|
||||
assign HotE = IMask & ~(IMask << 1'b1);
|
||||
assign HotEP1 = HotE >> 1'b1;
|
||||
|
||||
348
src/uncore/spi_controller.sv
Normal file
348
src/uncore/spi_controller.sv
Normal file
@ -0,0 +1,348 @@
|
||||
///////////////////////////////////////////
|
||||
// spi_controller.sv
|
||||
//
|
||||
// Written: jacobpease@protonmail.com
|
||||
// Created: October 28th, 2024
|
||||
// Modified:
|
||||
//
|
||||
// Purpose: Controller logic for SPI
|
||||
//
|
||||
// Documentation: RISC-V System on Chip Design
|
||||
//
|
||||
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||
// https://github.com/openhwgroup/cvw
|
||||
//
|
||||
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
//
|
||||
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||
// may obtain a copy of the License at
|
||||
//
|
||||
// https://solderpad.org/licenses/SHL-2.1/
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
// either express or implied. See the License for the specific language governing permissions
|
||||
// and limitations under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
module spi_controller (
|
||||
input logic PCLK,
|
||||
input logic PRESETn,
|
||||
input logic TransmitStart,
|
||||
input logic [11:0] SckDiv,
|
||||
input logic [1:0] SckMode,
|
||||
input logic [1:0] CSMode,
|
||||
input logic [15:0] Delay0,
|
||||
input logic [15:0] Delay1,
|
||||
input logic [7:0] txFIFORead,
|
||||
input logic txFIFOReadEmpty,
|
||||
output logic SPICLK,
|
||||
output logic SPIOUT,
|
||||
output logic CS
|
||||
);
|
||||
|
||||
// CSMode Stuff
|
||||
localparam HOLDMODE = 2'b10;
|
||||
localparam AUTOMODE = 2'b00;
|
||||
localparam OFFMODE = 2'b11;
|
||||
|
||||
typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
statetype CurrState, NextState;
|
||||
|
||||
// SCLKenable stuff
|
||||
logic [11:0] DivCounter;
|
||||
logic SCLKenable;
|
||||
logic SCLKenableEarly;
|
||||
logic SCLKenableLate;
|
||||
logic EdgeTiming;
|
||||
logic ZeroDiv;
|
||||
logic Clock0;
|
||||
logic Clock1;
|
||||
logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK!
|
||||
|
||||
|
||||
// Shift and Sample Edges
|
||||
logic PreShiftEdge;
|
||||
logic PreSampleEdge;
|
||||
logic ShiftEdge;
|
||||
logic SampleEdge;
|
||||
|
||||
// Frame stuff
|
||||
logic [2:0] BitNum;
|
||||
logic LastBit;
|
||||
logic EndOfFrame;
|
||||
logic EndOfFrameDelay;
|
||||
logic PhaseOneOffset;
|
||||
|
||||
// Transmit Stuff
|
||||
logic ContinueTransmit;
|
||||
|
||||
// SPIOUT Stuff
|
||||
logic TransmitLoad;
|
||||
logic [7:0] TransmitReg;
|
||||
logic Transmitting;
|
||||
logic EndTransmission;
|
||||
|
||||
logic HoldMode;
|
||||
|
||||
// Delay Stuff
|
||||
logic [7:0] cssck;
|
||||
logic [7:0] sckcs;
|
||||
logic [7:0] intercs;
|
||||
logic [7:0] interxfr;
|
||||
|
||||
logic HasCSSCK;
|
||||
logic HasSCKCS;
|
||||
logic HasINTERCS;
|
||||
logic HasINTERXFR;
|
||||
|
||||
logic EndOfCSSCK;
|
||||
logic EndOfSCKCS;
|
||||
logic EndOfINTERCS;
|
||||
logic EndOfINTERXFR;
|
||||
|
||||
logic [7:0] CSSCKCounter;
|
||||
logic [7:0] SCKCSCounter;
|
||||
logic [7:0] INTERCSCounter;
|
||||
logic [7:0] INTERXFRCounter;
|
||||
|
||||
logic DelayIsNext;
|
||||
|
||||
// Convenient Delay Reg Names
|
||||
assign cssck = Delay0[7:0];
|
||||
assign sckcs = Delay0[15:8];
|
||||
assign intercs = Delay1[7:0];
|
||||
assign interxfr = Delay1[15:8];
|
||||
|
||||
// Do we have delay for anything?
|
||||
assign HasCSSCK = cssck > 8'b0;
|
||||
assign HasSCKCS = sckcs > 8'b0;
|
||||
assign HasINTERCS = intercs > 8'b0;
|
||||
assign HasINTERXFR = interxfr > 8'b0;
|
||||
|
||||
// Have we hit full delay for any of the delays?
|
||||
assign EndOfCSSCK = CSSCKCounter == cssck;
|
||||
assign EndOfSCKCS = SCKCSCounter == sckcs;
|
||||
assign EndOfINTERCS = INTERCSCounter == intercs;
|
||||
assign EndOfINTERXFR = INTERXFRCounter == interxfr;
|
||||
|
||||
// Clock Signal Stuff -----------------------------------------------
|
||||
// I'm going to handle all clock stuff here, including ShiftEdge and
|
||||
// SampleEdge. This makes sure that SPICLK is an output of a register
|
||||
// and it properly synchronizes signals.
|
||||
|
||||
assign SCLKenableLate = DivCounter > SckDiv;
|
||||
assign SCLKenable = DivCounter == SckDiv;
|
||||
assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
|
||||
assign LastBit = BitNum == 3'd7;
|
||||
assign EdgeTiming = SckDiv > 12'b0 ? SCLKenableEarly : SCLKenable;
|
||||
|
||||
//assign SPICLK = Clock0;
|
||||
|
||||
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrame;
|
||||
assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay;
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
DivCounter <= 12'b0;
|
||||
SPICLK <= SckMode[1];
|
||||
SCK <= 0;
|
||||
BitNum <= 3'h0;
|
||||
PreShiftEdge <= 0;
|
||||
PreSampleEdge <= 0;
|
||||
EndOfFrame <= 0;
|
||||
end else begin
|
||||
// TODO: Consolidate into one delay counter since none of the
|
||||
// delays happen at the same time?
|
||||
if (TransmitStart) begin
|
||||
SCK <= 0;
|
||||
end else if (SCLKenable) begin
|
||||
SCK <= ~SCK;
|
||||
end
|
||||
|
||||
if ((CurrState == CSSCK) & SCK) begin
|
||||
CSSCKCounter <= CSSCKCounter + 8'd1;
|
||||
end else begin
|
||||
CSSCKCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == SCKCS) & SCK) begin
|
||||
SCKCSCounter <= SCKCSCounter + 8'd1;
|
||||
end else begin
|
||||
SCKCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERCS) & SCK) begin
|
||||
INTERCSCounter <= INTERCSCounter + 8'd1;
|
||||
end else begin
|
||||
INTERCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERXFR) & SCK) begin
|
||||
INTERXFRCounter <= INTERXFRCounter + 8'd1;
|
||||
end else begin
|
||||
INTERXFRCounter <= 8'd0;
|
||||
end
|
||||
|
||||
// SPICLK Logic
|
||||
if (TransmitStart) begin
|
||||
SPICLK <= SckMode[1];
|
||||
end else if (SCLKenable & Transmitting) begin
|
||||
SPICLK <= (~EndTransmission & ~DelayIsNext) ? ~SPICLK : SckMode[1];
|
||||
end
|
||||
|
||||
// Reset divider
|
||||
if (SCLKenable | TransmitStart) begin
|
||||
DivCounter <= 12'b0;
|
||||
end else begin
|
||||
DivCounter = DivCounter + 12'd1;
|
||||
end
|
||||
|
||||
// EndOfFrame controller
|
||||
// if (SckDiv > 0 ? SCLKenableEarly & LastBit & SPICLK : LastBit & ~SPICLK) begin
|
||||
// EndOfFrame <= 1'b1;
|
||||
// end else begin
|
||||
// EndOfFrame <= 1'b0;
|
||||
// end
|
||||
|
||||
if (~TransmitStart) begin
|
||||
EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
end
|
||||
|
||||
// Increment BitNum
|
||||
if (ShiftEdge & Transmitting) begin
|
||||
BitNum <= BitNum + 3'd1;
|
||||
end else if (EndOfFrameDelay) begin
|
||||
BitNum <= 3'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Delay ShiftEdge and SampleEdge by a half PCLK period
|
||||
// Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges.
|
||||
// Sweeeeeeeeeet...
|
||||
always_ff @(posedge ~PCLK) begin
|
||||
if (~PRESETn | TransmitStart) begin
|
||||
ShiftEdge <= 0;
|
||||
PhaseOneOffset <= 0;
|
||||
SampleEdge <= 0;
|
||||
EndOfFrameDelay <= 0;
|
||||
end else begin
|
||||
ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
|
||||
PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : PhaseOneOffset;
|
||||
SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting;
|
||||
EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
end
|
||||
end
|
||||
|
||||
// typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
// statetype CurrState, NextState;
|
||||
|
||||
assign HoldMode = CSMode == 2'b10;
|
||||
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
CurrState <= INACTIVE;
|
||||
end else if (SCLKenable) begin
|
||||
CurrState <= NextState;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
case (CurrState)
|
||||
INACTIVE: begin // INACTIVE case --------------------------------
|
||||
if (TransmitStart) begin
|
||||
if (~HasCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end else begin
|
||||
NextState = CSSCK;
|
||||
end
|
||||
end else begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
end
|
||||
CSSCK: begin // DELAY0 case -------------------------------------
|
||||
if (EndOfCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
TRANSMIT: begin // TRANSMIT case --------------------------------
|
||||
case(CSMode)
|
||||
AUTOMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (ContinueTransmit) begin
|
||||
NextState = SCKCS;
|
||||
end
|
||||
end
|
||||
HOLDMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (HasINTERXFR) NextState = INTERXFR;
|
||||
end
|
||||
end
|
||||
OFFMODE: begin
|
||||
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
SCKCS: begin // SCKCS case --------------------------------------
|
||||
if (EndOfSCKCS) begin
|
||||
if (EndTransmission) begin
|
||||
if (CSMode == AUTOMODE) NextState = INACTIVE;
|
||||
else if (CSMode == HOLDMODE) NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (HasINTERCS) NextState = INTERCS;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
HOLD: begin // HOLD mode case -----------------------------------
|
||||
if (CSMode == AUTOMODE) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (TransmitStart) begin // If FIFO is written to, start again.
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
INTERCS: begin // INTERCS case ----------------------------------
|
||||
if (EndOfINTERCS) begin
|
||||
if (HasCSSCK) NextState = CSSCK;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
INTERXFR: begin // INTERXFR case --------------------------------
|
||||
if (EndOfINTERXFR) begin
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign Transmitting = CurrState == TRANSMIT;
|
||||
assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR);
|
||||
|
||||
//
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
TransmitReg <= 8'b0;
|
||||
end else if (TransmitLoad) begin
|
||||
TransmitReg <= txFIFORead;
|
||||
end else if (ShiftEdge) begin
|
||||
TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
|
||||
end
|
||||
end
|
||||
|
||||
assign SPIOUT = TransmitReg[7];
|
||||
assign CS = CurrState == INACTIVE | CurrState == INTERCS;
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue
Block a user