-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
add JTAG support to the core
1 parent
14620c8
commit 3a57205
Showing
35 changed files
with
3,823 additions
and
11,566 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,3 +65,6 @@ log | |
vivado_* | ||
tight_setup_hold_pins.txt | ||
.Xil | ||
|
||
# Verilator debug output | ||
simulation_dump.vcd |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,5 +13,6 @@ opt-level = "z" | |
[workspace] | ||
members = [ | ||
"clash-vexriscv-sim/test-programs", | ||
"debug-test", | ||
] | ||
resolver = "2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ packages: | |
clash-vexriscv-sim/ | ||
|
||
write-ghc-environment-files: always | ||
|
||
tests: True | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
-- | Convert an ELF file to a set of @.mem@ files, suitable for use with | ||
-- Verilog simulators. Use in combination with @readmemh@ in Verilog. | ||
module Main where | ||
|
||
import Prelude | ||
|
||
import Control.Concurrent.Async (mapConcurrently_) | ||
import Control.Monad (forM_) | ||
import Data.IntMap (IntMap, findWithDefault) | ||
import Data.Tuple.Extra (uncurry3) | ||
import System.Environment (getArgs) | ||
import System.Exit (die) | ||
import System.IO (withFile, IOMode(WriteMode), hPutStrLn) | ||
import Text.Printf (printf) | ||
import Text.Read (readMaybe) | ||
import Utils.ReadElf (readElfFromMemory) | ||
|
||
import qualified Data.ByteString as BS | ||
import qualified Clash.Prelude as C | ||
|
||
iMEM_START :: Int | ||
iMEM_START = 0x20000000 | ||
|
||
dMEM_START :: Int | ||
dMEM_START = 0x40000000 | ||
|
||
-- | Like 'zipWithM_', but execute concurrently | ||
zipWithConcurrently_ :: (a -> b -> IO ()) -> [a] -> [b] -> IO () | ||
zipWithConcurrently_ f xs ys = mapConcurrently_ (uncurry f) (zip xs ys) | ||
|
||
-- | Like 'zipWith3M_', but execute concurrently | ||
zipWith3Concurrently_ :: (a -> b -> c -> IO ()) -> [a] -> [b] -> [c] -> IO () | ||
zipWith3Concurrently_ f xs ys zs = mapConcurrently_ (uncurry3 f) (zip3 xs ys zs) | ||
|
||
-- | Convenience function to get data from a memory map. If a memory address is | ||
-- not found, return 0. | ||
getData :: Num a => IntMap a -> Int -> a | ||
getData mem addr = findWithDefault 0 addr mem | ||
|
||
-- | Generate the addresses for the four memory banks, given the total size in | ||
-- bytes, and the start address. | ||
getAddrs :: Int -> Int -> ([Int], [Int], [Int], [Int]) | ||
getAddrs size start = | ||
( [start + 0, start + 4 .. start + size - 1] | ||
, [start + 1, start + 5 .. start + size - 1] | ||
, [start + 2, start + 6 .. start + size - 1] | ||
, [start + 3, start + 7 .. start + size - 1] ) | ||
|
||
-- | Write a single @.mem@ file | ||
writeByteMem :: IntMap (C.BitVector 8) -> FilePath -> [Int] -> IO () | ||
writeByteMem mem path addrs = do | ||
putStrLn $ "Writing " <> path | ||
withFile path WriteMode $ \h -> | ||
forM_ addrs $ \addr -> do | ||
hPutStrLn h (toHex (getData mem addr)) | ||
|
||
-- | Write four @.mem@ files | ||
writeMem :: Int -> IntMap (C.BitVector 8) -> FilePath -> Int -> IO () | ||
writeMem size mem prefix start = do | ||
let (addrs0, addrs1, addrs2, addrs3) = getAddrs size start | ||
|
||
zipWithConcurrently_ | ||
(writeByteMem mem) | ||
[prefix <> show n <> ".mem" | n <- [(0::Int)..]] | ||
[addrs0, addrs1, addrs2, addrs3] | ||
|
||
-- | Print a byte as a hex string of the form 0x00. | ||
toHex :: C.BitVector 8 -> String | ||
toHex = printf "0x%02x" . toInteger | ||
|
||
main :: IO () | ||
main = do | ||
let usage = "Usage: elf-to-hex <size> <elf-file>" | ||
|
||
(sizeStr, elfFile) <- getArgs >>= \case | ||
[sizeStr, elfFile] -> pure (sizeStr, elfFile) | ||
args -> die $ usage <> "\n\n" <> "Expected 2 arguments, but got " <> show (length args) | ||
|
||
size <- case readMaybe @Int sizeStr of | ||
Just x -> pure x | ||
Nothing -> die $ usage <> "\n\n" <> "<size> must be an integer, but got: " <> sizeStr | ||
(_addr, iMem, dMem) <- readElfFromMemory <$> BS.readFile elfFile | ||
|
||
zipWith3Concurrently_ | ||
(writeMem size) | ||
[iMem, dMem] | ||
["imem", "dmem"] | ||
[iMEM_START, dMEM_START] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 0} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
debug: !!vexriscv.DebugReport {hardwareBreakpointCount: 0} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
`timescale 1ns/1ps | ||
|
||
module VexRiscv ( | ||
input timerInterrupt, | ||
input externalInterrupt, | ||
input softwareInterrupt, | ||
output debug_resetOut, | ||
output iBusWishbone_CYC, | ||
output iBusWishbone_STB, | ||
input iBusWishbone_ACK, | ||
output iBusWishbone_WE, | ||
output [29:0] iBusWishbone_ADR, | ||
input [31:0] iBusWishbone_DAT_MISO, | ||
output [31:0] iBusWishbone_DAT_MOSI, | ||
output [3:0] iBusWishbone_SEL, | ||
input iBusWishbone_ERR, | ||
output [2:0] iBusWishbone_CTI, | ||
output [1:0] iBusWishbone_BTE, | ||
output dBusWishbone_CYC, | ||
output dBusWishbone_STB, | ||
input dBusWishbone_ACK, | ||
output dBusWishbone_WE, | ||
output [29:0] dBusWishbone_ADR, | ||
input [31:0] dBusWishbone_DAT_MISO, | ||
output [31:0] dBusWishbone_DAT_MOSI, | ||
output reg [3:0] dBusWishbone_SEL, | ||
input dBusWishbone_ERR, | ||
output [2:0] dBusWishbone_CTI, | ||
output [1:0] dBusWishbone_BTE, | ||
input jtag_tms, | ||
input jtag_tdi, | ||
output jtag_tdo, | ||
input jtag_tck, | ||
input clk, | ||
input reset | ||
); | ||
initial begin | ||
// Specify the dump file name | ||
$dumpfile("simulation_dump.vcd"); | ||
|
||
// Dump all signals to the VCD file | ||
$dumpvars(1, VexRiscv); | ||
end | ||
|
||
wire i_iBusWishbone_ACK; | ||
reg [31:0] i_iBusWishbone_DAT_MISO; | ||
wire i_iBusWishbone_ERR = 1'b0; | ||
|
||
wire i_dBusWishbone_ACK; | ||
reg [31:0] i_dBusWishbone_DAT_MISO; | ||
wire i_dBusWishbone_ERR = 1'b0; | ||
|
||
reg reset_cpu; | ||
wire reqCpuReset; | ||
|
||
// Handle reset logic in Verilog instead of Haskell | ||
assign debug_resetOut = 1'b0; | ||
always @(posedge clk) begin | ||
reset_cpu <= reset || reqCpuReset; | ||
end | ||
|
||
// Memory parameters | ||
localparam dmem_lower = 'h40000000 / 4; | ||
localparam imem_lower = 'h20000000 / 4; | ||
localparam mem_size_bytes = 1048576; // 1 MiB | ||
localparam mem_size_words = mem_size_bytes / 4; | ||
localparam mem_addr_bits = 18; // log2(mem_size_words) | ||
localparam dmem_upper = dmem_lower + mem_size_words; | ||
localparam imem_upper = imem_lower + mem_size_words; | ||
|
||
//============================================================ | ||
// Data memory | ||
//============================================================ | ||
reg [7:0] dmem0[0:mem_size_words-1]; | ||
reg [7:0] dmem1[0:mem_size_words-1]; | ||
reg [7:0] dmem2[0:mem_size_words-1]; | ||
reg [7:0] dmem3[0:mem_size_words-1]; | ||
|
||
initial begin | ||
$readmemh("dmem0.mem", dmem0); | ||
$readmemh("dmem1.mem", dmem1); | ||
$readmemh("dmem2.mem", dmem2); | ||
$readmemh("dmem3.mem", dmem3); | ||
end | ||
|
||
// A commmand is valid on CYC and STB, and the response is valid on the next cycle | ||
// provided that CYC and STB are still asserted. | ||
wire dBus_cmd_valid; | ||
reg prev_dBus_cmd_valid = 1'b0; | ||
always @(posedge clk) begin | ||
prev_dBus_cmd_valid <= dBus_cmd_valid; | ||
end | ||
|
||
reg dBus_rsp = 1'b0; | ||
assign dBus_cmd_valid = dBusWishbone_CYC && dBusWishbone_STB && !prev_dBus_cmd_valid; | ||
assign i_dBusWishbone_ACK = dBus_rsp && !i_dBusWishbone_ERR; | ||
assign i_dBusWishbone_ERR = !(valid_data_address_d || valid_instr_address_d); | ||
wire [mem_addr_bits-1:0] dmem_addr; | ||
assign dmem_addr = dBusWishbone_ADR[mem_addr_bits-1:0]; | ||
|
||
wire valid_data_address_d, valid_instr_address_d; | ||
assign valid_data_address_d = dmem_lower <= dBusWishbone_ADR && dBusWishbone_ADR < dmem_upper; | ||
assign valid_instr_address_d = imem_lower <= dBusWishbone_ADR && dBusWishbone_ADR < imem_upper; | ||
|
||
always @(posedge clk) begin | ||
i_dBusWishbone_DAT_MISO[ 7: 0] <= 8'b00000000; | ||
i_dBusWishbone_DAT_MISO[15: 8] <= 8'b00000000; | ||
i_dBusWishbone_DAT_MISO[23:16] <= 8'b00000000; | ||
i_dBusWishbone_DAT_MISO[31:24] <= 8'b00000000; | ||
dBus_rsp <= dBus_cmd_valid; | ||
|
||
if (dBus_cmd_valid) begin | ||
if (valid_data_address_d) begin | ||
// DAT | ||
i_dBusWishbone_DAT_MISO[ 7: 0] <= dmem0[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[15: 8] <= dmem1[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[23:16] <= dmem2[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[31:24] <= dmem3[dmem_addr]; | ||
|
||
if (dBusWishbone_WE) begin | ||
if (dBusWishbone_SEL[0]) begin | ||
dmem0[dmem_addr] <= dBusWishbone_DAT_MOSI[ 7: 0]; | ||
i_dBusWishbone_DAT_MISO[ 7: 0] <= dBusWishbone_DAT_MOSI[ 7: 0]; | ||
end | ||
|
||
if (dBusWishbone_SEL[1]) begin | ||
dmem1[dmem_addr] <= dBusWishbone_DAT_MOSI[15: 8]; | ||
i_dBusWishbone_DAT_MISO[15: 8] <= dBusWishbone_DAT_MOSI[15: 8]; | ||
end | ||
|
||
if (dBusWishbone_SEL[2]) begin | ||
dmem2[dmem_addr] <= dBusWishbone_DAT_MOSI[23:16]; | ||
i_dBusWishbone_DAT_MISO[23:16] <= dBusWishbone_DAT_MOSI[23:16]; | ||
end | ||
|
||
if (dBusWishbone_SEL[3]) begin | ||
dmem3[dmem_addr] <= dBusWishbone_DAT_MOSI[31:24]; | ||
i_dBusWishbone_DAT_MISO[31:24] <= dBusWishbone_DAT_MOSI[31:24]; | ||
end | ||
end | ||
|
||
else | ||
if (valid_instr_address_d) begin | ||
// INSTR | ||
i_dBusWishbone_DAT_MISO[ 7: 0] <= imem0[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[15: 8] <= imem1[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[23:16] <= imem2[dmem_addr]; | ||
i_dBusWishbone_DAT_MISO[31:24] <= imem3[dmem_addr]; | ||
|
||
if (dBusWishbone_WE) begin | ||
if (iBusWishbone_SEL[0]) begin | ||
imem0[dmem_addr] <= dBusWishbone_DAT_MOSI[ 7: 0]; | ||
i_dBusWishbone_DAT_MISO[ 7: 0] <= dBusWishbone_DAT_MOSI[ 7: 0]; | ||
end | ||
|
||
if (iBusWishbone_SEL[1]) begin | ||
imem1[dmem_addr] <= dBusWishbone_DAT_MOSI[15: 8]; | ||
i_dBusWishbone_DAT_MISO[15: 8] <= dBusWishbone_DAT_MOSI[15: 8]; | ||
end | ||
|
||
if (iBusWishbone_SEL[2]) begin | ||
imem2[dmem_addr] <= dBusWishbone_DAT_MOSI[23:16]; | ||
i_dBusWishbone_DAT_MISO[23:16] <= dBusWishbone_DAT_MOSI[23:16]; | ||
end | ||
|
||
if (iBusWishbone_SEL[3]) begin | ||
imem3[dmem_addr] <= dBusWishbone_DAT_MOSI[31:24]; | ||
i_dBusWishbone_DAT_MISO[31:24] <= dBusWishbone_DAT_MOSI[31:24]; | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
//============================================================ | ||
// Instruction memory | ||
//============================================================ | ||
reg [7:0] imem0[0:mem_size_words-1]; | ||
reg [7:0] imem1[0:mem_size_words-1]; | ||
reg [7:0] imem2[0:mem_size_words-1]; | ||
reg [7:0] imem3[0:mem_size_words-1]; | ||
|
||
initial begin | ||
$readmemh("imem0.mem", imem0); | ||
$readmemh("imem1.mem", imem1); | ||
$readmemh("imem2.mem", imem2); | ||
$readmemh("imem3.mem", imem3); | ||
end | ||
|
||
// A commmand is valid on CYC and STB, and the response is valid on the next cycle | ||
// provided that CYC and STB are still asserted. | ||
wire iBus_cmd_valid; | ||
reg prev_iBus_cmd_valid; | ||
reg prev_iBus_rsp_valid; | ||
always @(posedge clk) begin | ||
prev_iBus_cmd_valid <= iBus_cmd_valid; | ||
prev_iBus_rsp_valid <= iBus_rsp_valid; | ||
end | ||
|
||
wire valid_instr_address_i; | ||
assign valid_instr_address_i = imem_lower <= iBusWishbone_ADR && iBusWishbone_ADR < imem_upper; | ||
|
||
assign iBus_cmd_valid = iBusWishbone_CYC && iBusWishbone_STB; | ||
wire iBus_rsp_valid; | ||
assign iBus_rsp_valid = prev_iBus_cmd_valid && iBus_cmd_valid && !prev_iBus_rsp_valid; | ||
assign i_iBusWishbone_ACK = iBus_rsp_valid && valid_instr_address_i; | ||
assign i_iBusWishbone_ERR = !valid_instr_address_i; | ||
wire [mem_addr_bits-1:0] imem_addr; | ||
assign imem_addr = iBusWishbone_ADR[mem_addr_bits-1:0]; | ||
|
||
always @(posedge clk) begin | ||
i_iBusWishbone_DAT_MISO[ 7: 0] <= imem0[imem_addr]; | ||
i_iBusWishbone_DAT_MISO[15: 8] <= imem1[imem_addr]; | ||
i_iBusWishbone_DAT_MISO[23:16] <= imem2[imem_addr]; | ||
i_iBusWishbone_DAT_MISO[31:24] <= imem3[imem_addr]; | ||
end | ||
|
||
//============================================================ | ||
// CPU instantiation | ||
//============================================================ | ||
VexRiscvInner VexRiscvInner | ||
( .timerInterrupt (timerInterrupt) | ||
, .externalInterrupt (externalInterrupt) | ||
, .softwareInterrupt (softwareInterrupt) | ||
, .debug_resetOut (reqCpuReset) | ||
|
||
, .iBusWishbone_CYC (iBusWishbone_CYC) // o | ||
, .iBusWishbone_STB (iBusWishbone_STB) // o | ||
, .iBusWishbone_ACK (i_iBusWishbone_ACK) // i | ||
, .iBusWishbone_WE (iBusWishbone_WE) // o | ||
, .iBusWishbone_ADR (iBusWishbone_ADR) // o | ||
, .iBusWishbone_DAT_MISO (i_iBusWishbone_DAT_MISO) // i | ||
, .iBusWishbone_DAT_MOSI (iBusWishbone_DAT_MOSI) // o | ||
, .iBusWishbone_SEL (iBusWishbone_SEL) // o | ||
, .iBusWishbone_ERR (i_iBusWishbone_ERR) // i | ||
, .iBusWishbone_CTI (iBusWishbone_CTI) // o | ||
, .iBusWishbone_BTE (iBusWishbone_BTE) // o | ||
|
||
, .dBusWishbone_CYC (dBusWishbone_CYC) // o | ||
, .dBusWishbone_STB (dBusWishbone_STB) // o | ||
, .dBusWishbone_ACK (i_dBusWishbone_ACK) // i | ||
, .dBusWishbone_WE (dBusWishbone_WE) // o | ||
, .dBusWishbone_ADR (dBusWishbone_ADR) // o | ||
, .dBusWishbone_DAT_MISO (i_dBusWishbone_DAT_MISO) // i | ||
, .dBusWishbone_DAT_MOSI (dBusWishbone_DAT_MOSI) // o | ||
, .dBusWishbone_SEL (dBusWishbone_SEL) // o | ||
, .dBusWishbone_ERR (i_dBusWishbone_ERR) // i | ||
, .dBusWishbone_CTI (dBusWishbone_CTI) // o | ||
, .dBusWishbone_BTE (dBusWishbone_BTE) // o | ||
|
||
, .jtag_tms (jtag_tms) | ||
, .jtag_tdi (jtag_tdi) | ||
, .jtag_tdo (jtag_tdo) | ||
, .jtag_tck (jtag_tck) | ||
, .clk (clk) | ||
, .reset (reset_cpu) | ||
); | ||
|
||
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
-- SPDX-FileCopyrightText: 2023 Google LLC | ||
-- | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
|
||
{-# LANGUAGE RecordWildCards #-} | ||
module VexRiscv.JtagTcpBridge (vexrJtagBridge, defaultIn) where | ||
|
||
import Clash.Prelude | ||
|
||
import Clash.Signal.Internal | ||
|
||
import VexRiscv | ||
import VexRiscv.FFI | ||
|
||
import Foreign | ||
import System.IO.Unsafe (unsafePerformIO) | ||
import Network.Socket (PortNumber) | ||
|
||
defaultIn :: JtagIn | ||
defaultIn = JtagIn { testClock = low, testModeSelect = low, testDataIn = low } | ||
|
||
{-# NOINLINE inner #-} | ||
inner :: (t -> IO JtagIn) -> Signal dom t -> Signal dom JtagIn | ||
inner jtagBridgeStep (o :- outs) = unsafePerformIO $ do | ||
in' <- jtagBridgeStep o | ||
let ins' = inner jtagBridgeStep outs | ||
pure $ in' :- (in' `deepseqX` ins') | ||
|
||
vexrJtagBridge :: PortNumber -> Signal dom JtagOut -> Signal dom JtagIn | ||
vexrJtagBridge port out = inner jtagBridgeStep out | ||
where | ||
(_, jtagBridgeStep) = unsafePerformIO $ vexrJtagBridge' port | ||
|
||
vexrJtagBridge' :: | ||
PortNumber -> | ||
IO ( IO () -- ^ delete function | ||
, JtagOut -> IO JtagIn -- ^ step function | ||
) | ||
vexrJtagBridge' port = do | ||
bridge <- vexrJtagBridgeInit (fromIntegral port) | ||
let | ||
shutDown = vexrJtagBridgeShutdown bridge | ||
|
||
step JtagOut{..} = alloca $ \outFFI -> alloca $ \inFFI -> do | ||
poke outFFI (JTAG_OUTPUT debugReset testDataOut) | ||
vexrJtagBridgeStep bridge outFFI inFFI | ||
JTAG_INPUT{..} <- peek inFFI | ||
let input = JtagIn { testClock = tck, testModeSelect = tms, testDataIn = tdi } | ||
pure input | ||
pure (shutDown, step) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
-- SPDX-FileCopyrightText: 2023 Google LLC | ||
-- | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
|
||
module VexRiscv.JtagTcpBridge where | ||
|
||
import Clash.Prelude | ||
|
||
import Clash.Signal.Internal | ||
import Network.Socket | ||
import Protocols | ||
import Protocols.Internal (CSignal(..), Reverse) | ||
import VexRiscv (JtagIn(..), JtagOut(..), Jtag) | ||
import Control.Concurrent (MVar, forkIO, newEmptyMVar, putMVar, takeMVar, tryTakeMVar) | ||
import Control.Monad (when) | ||
import Network.Socket.ByteString (sendAll, recv) | ||
|
||
import qualified Data.ByteString as BS | ||
import System.IO.Unsafe (unsafePerformIO) | ||
import Debug.Trace (trace) | ||
|
||
data NetworkThreadToMainMsg | ||
= Connected | ||
| Disconnected | ||
| DataReceived [BitVector 8] | ||
|
||
data MainToNetworkThreadMsg | ||
= ReadMore | ||
| Send (BitVector 8) | ||
|
||
data NetworkThreadState | ||
= NoClient | ||
| PerformRead Socket | ||
| WaitForNextRead Socket | ||
deriving (Show) | ||
|
||
data MainThreadState | ||
= MDisconnected | ||
| MWaitForRead | ||
| MProcessing (BitVector 2) [BitVector 8] | ||
deriving (Show) | ||
|
||
jtagTcpBridge :: | ||
(HiddenClockResetEnable dom) => | ||
PortNumber -> | ||
Circuit | ||
(Jtag dom, Reverse (CSignal dom Bool)) | ||
() | ||
jtagTcpBridge port = | ||
Circuit $ \((jtagOut, _), ()) -> unsafePerformIO $ do | ||
(enable, jtagIn) <- jtagTcpBridge' port jtagOut | ||
pure ((jtagIn, CSignal $ fromEnable enable), ()) | ||
|
||
jtagTcpBridge' :: | ||
(KnownDomain dom) => | ||
PortNumber -> | ||
Signal dom JtagOut -> | ||
IO (Enable dom, Signal dom JtagIn) | ||
jtagTcpBridge' port jtagOut = do | ||
|
||
(n2m, m2n) <- server port | ||
|
||
(unbundle -> (enable, jtagIn)) <- client n2m m2n MDisconnected jtagOut | ||
|
||
pure (toEnable enable, jtagIn) | ||
|
||
{-# NOINLINE jtagTcpBridge' #-} | ||
|
||
server :: PortNumber -> IO (MVar NetworkThreadToMainMsg, MVar MainToNetworkThreadMsg) | ||
server port = withSocketsDo $ do | ||
sock <- setup | ||
|
||
threadToMainChan <- newEmptyMVar | ||
mainToThreadChan <- newEmptyMVar | ||
|
||
let | ||
thread NoClient = do | ||
(clientSock, _) <- accept sock | ||
putMVar threadToMainChan Connected | ||
thread (PerformRead clientSock) | ||
thread (PerformRead clientSock) = do | ||
buf <- recv clientSock 100 | ||
if BS.null buf then do | ||
putMVar threadToMainChan Disconnected | ||
thread NoClient | ||
else do | ||
let dat = pack <$> BS.unpack buf | ||
putMVar threadToMainChan (DataReceived dat) | ||
thread (WaitForNextRead clientSock) | ||
|
||
thread (WaitForNextRead clientSock) = do | ||
msg <- takeMVar mainToThreadChan | ||
case msg of | ||
ReadMore -> thread (PerformRead clientSock) | ||
Send byte -> do | ||
sendAll clientSock (BS.singleton $ unpack byte) | ||
thread (WaitForNextRead clientSock) | ||
|
||
_ <- forkIO $ thread NoClient | ||
|
||
pure (threadToMainChan, mainToThreadChan) | ||
|
||
where | ||
setup = do | ||
sock <- socket AF_INET Stream 0 | ||
|
||
setSocketOption sock NoDelay 0 | ||
|
||
bind sock (SockAddrInet port (tupleToHostAddress (127, 0, 0, 1))) | ||
|
||
listen sock 1 | ||
|
||
pure sock | ||
|
||
defaultIn :: JtagIn | ||
defaultIn = JtagIn { testModeSelect = low, testDataIn = low } | ||
|
||
dbg :: Show a => a -> a | ||
dbg x = | ||
trace (show x) | ||
x | ||
|
||
clientSleep :: BitVector 2 | ||
clientSleep = 4 | ||
|
||
client :: | ||
(KnownDomain dom) => | ||
MVar NetworkThreadToMainMsg -> | ||
MVar MainToNetworkThreadMsg -> | ||
MainThreadState -> | ||
Signal dom JtagOut -> | ||
IO (Signal dom (Bool, JtagIn)) | ||
client n2m m2n MDisconnected (_out :- outs) = do | ||
msg <- tryTakeMVar n2m | ||
case msg of | ||
Nothing -> | ||
pure $ _out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n MDisconnected outs) | ||
Just Connected -> do | ||
pure $ _out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n MWaitForRead outs) | ||
Just Disconnected -> do | ||
errorX "????" | ||
Just (DataReceived _xs) -> do | ||
errorX "????" | ||
|
||
client n2m m2n MWaitForRead (out :- outs) = do | ||
msg <- tryTakeMVar n2m | ||
case msg of | ||
Nothing -> | ||
pure $ out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n MWaitForRead outs) | ||
Just Disconnected -> | ||
pure $ out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n MDisconnected outs) | ||
Just (DataReceived xs) -> | ||
client n2m m2n (MProcessing 0 xs) (out :- outs) | ||
Just Connected -> | ||
errorX "????" | ||
|
||
client n2m m2n (MProcessing _ []) (out :- outs) = do | ||
putMVar m2n ReadMore | ||
pure $ out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n MWaitForRead outs) | ||
client n2m m2n (MProcessing 0 (x:xs)) (out :- outs) = do | ||
let tms = x ! (0 :: Int) | ||
tdi = x ! (1 :: Int) | ||
tck = x ! (3 :: Int) | ||
|
||
sendTdo = bitToBool $ x ! (2 :: Int) | ||
|
||
enable = bitToBool tck | ||
|
||
when sendTdo $ do | ||
let tdo = bitToBool $ testDataOut out | ||
-- putStrLn $ "send TDO " <> show tdo | ||
putMVar m2n $ Send $ boolToBV tdo | ||
|
||
let inDat = JtagIn { testModeSelect = tms, testDataIn = tdi } | ||
|
||
when enable $ do | ||
-- putStrLn "Enable" | ||
-- putStrLn $ "IN " <> showX inDat | ||
pure () | ||
|
||
pure $ (enable, inDat) :- unsafePerformIO (client n2m m2n (MProcessing clientSleep xs) outs) | ||
client n2m m2n (MProcessing n xs) (out :- outs) = do | ||
pure $ out `deepseqX` (False, defaultIn) :- unsafePerformIO (client n2m m2n (MProcessing (n - 1) xs) outs) | ||
|
||
|
||
{-# NOINLINE client #-} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# SPDX-FileCopyrightText: 2022 Google LLC | ||
# | ||
# SPDX-License-Identifier: CC0-1.0 | ||
|
||
[package] | ||
name = "debug-test" | ||
version = "0.1.0" | ||
edition = "2021" | ||
license = "Apache-2.0" | ||
authors = ["Google LLC"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
riscv-rt = "0.11.0" | ||
riscv = "^0.10" | ||
heapless = { version = "0.7", default-features = false } | ||
panic-halt = "0.2.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-FileCopyrightText: 2022 Google LLC | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use std::env; | ||
use std::fs; | ||
use std::path::Path; | ||
|
||
/// Put the linker script somewhere the linker can find it. | ||
fn main() { | ||
let out_dir = env::var("OUT_DIR").expect("No out dir"); | ||
let dest_path = Path::new(&out_dir).join("memory.x"); | ||
fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); | ||
|
||
if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { | ||
println!("cargo:rustc-link-arg=-Tmemory.x"); | ||
println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt | ||
} | ||
println!("cargo:rustc-link-search={out_dir}"); | ||
|
||
println!("cargo:rerun-if-changed=memory.x"); | ||
println!("cargo:rerun-if-changed=build.rs"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
SPDX-FileCopyrightText: 2022 Google LLC | ||
SPDX-License-Identifier: CC0-1.0 | ||
*/ | ||
|
||
MEMORY | ||
{ | ||
DATA : ORIGIN = 0x40000000, LENGTH = 512K | ||
INSTR : ORIGIN = 0x20000000, LENGTH = 512K | ||
} | ||
|
||
REGION_ALIAS("REGION_TEXT", INSTR); | ||
REGION_ALIAS("REGION_RODATA", DATA); | ||
REGION_ALIAS("REGION_DATA", DATA); | ||
REGION_ALIAS("REGION_BSS", DATA); | ||
REGION_ALIAS("REGION_HEAP", DATA); | ||
REGION_ALIAS("REGION_STACK", DATA); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-FileCopyrightText: 2022 Google LLC | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#![no_std] | ||
#![cfg_attr(not(test), no_main)] | ||
|
||
#[cfg(not(test))] | ||
use riscv_rt::entry; | ||
|
||
#[cfg(not(test))] | ||
extern crate panic_halt; | ||
|
||
const ADDR: *mut u8 = 0x0000_1000 as *mut u8; | ||
|
||
fn print(s: &str) { | ||
for b in s.bytes() { | ||
unsafe { | ||
ADDR.write_volatile(b); | ||
} | ||
} | ||
} | ||
|
||
#[cfg_attr(not(test), entry)] | ||
fn main() -> ! { | ||
print("[CPU] a\n"); | ||
loop { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-FileCopyrightText: 2022 Google LLC | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#![no_std] | ||
#![cfg_attr(not(test), no_main)] | ||
|
||
#[cfg(not(test))] | ||
use riscv_rt::entry; | ||
|
||
#[cfg(not(test))] | ||
extern crate panic_halt; | ||
|
||
const ADDR: *mut u8 = 0x0000_1000 as *mut u8; | ||
|
||
fn print(s: &str) { | ||
for b in s.bytes() { | ||
unsafe { | ||
ADDR.write_volatile(b); | ||
} | ||
} | ||
} | ||
|
||
#[cfg_attr(not(test), entry)] | ||
fn main() -> ! { | ||
print("[CPU] b\n"); | ||
loop {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// SPDX-FileCopyrightText: 2022 Google LLC | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#![no_std] | ||
#![cfg_attr(not(test), no_main)] | ||
|
||
use core::fmt::Write; | ||
|
||
use heapless::String; | ||
#[cfg(not(test))] | ||
use riscv_rt::entry; | ||
|
||
#[cfg(not(test))] | ||
extern crate panic_halt; | ||
|
||
const ADDR: *mut u8 = 0x0000_1000 as *mut u8; | ||
|
||
fn print(s: &str) { | ||
for b in s.bytes() { | ||
unsafe { | ||
ADDR.write_volatile(b); | ||
} | ||
} | ||
} | ||
|
||
#[cfg_attr(not(test), entry)] | ||
fn main() -> ! { | ||
print("hello, world.\n"); | ||
|
||
print("I am here to be debugged!\n"); | ||
|
||
loop { | ||
for i in 0..30 { | ||
let mut s = String::<16>::new(); | ||
let _ = writeln!(s, "Hey! {i}"); | ||
print(&s); | ||
} | ||
|
||
print("wheeeey!\n"); | ||
|
||
// unsafe { | ||
// riscv::asm::ebreak(); | ||
// } | ||
} | ||
} | ||
|
||
#[export_name = "UserSoft"] | ||
fn user_soft_handler() { | ||
loop { | ||
print("INTERRUPT UserSoft"); | ||
} | ||
} | ||
|
||
#[export_name = "MachineSoft"] | ||
fn machine_soft_handler() { | ||
loop { | ||
print("INTERRUPT MachineSoft"); | ||
} | ||
} | ||
|
||
#[export_name = "UserTimer"] | ||
fn user_timer_handler() { | ||
loop { | ||
print("INTERRUPT UserTimer"); | ||
} | ||
} | ||
|
||
#[export_name = "MachineTimer"] | ||
fn machine_timer_handler() { | ||
loop { | ||
print("INTERRUPT MachineTimer"); | ||
} | ||
} | ||
|
||
#[export_name = "UserExternal"] | ||
fn user_ext_handler() { | ||
loop { | ||
print("INTERRUPT UserExternal"); | ||
} | ||
} | ||
|
||
#[export_name = "MachineExternal"] | ||
fn machine_ext_handler() { | ||
loop { | ||
print("INTERRUPT MachineExternal"); | ||
} | ||
} | ||
|
||
#[export_name = "DefaultHandler"] | ||
fn default_handler() { | ||
loop { | ||
print("INTERRUPT default handler"); | ||
} | ||
} | ||
|
||
#[export_name = "ExceptionHandler"] | ||
fn exception_handler(_trap_frame: &riscv_rt::TrapFrame) -> ! { | ||
riscv::interrupt::free(|| { | ||
print("... caught an exception. Looping forever now.\n"); | ||
}); | ||
loop { | ||
// print(""); | ||
continue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# SPDX-FileCopyrightText: 2022-2023 Google LLC | ||
# | ||
# | ||
# SPDX-License-Identifier: CC0-1.0 | ||
|
||
[toolchain] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Assume "print_a" is running on the CPU | ||
file "target/riscv32imc-unknown-none-elf/release/print_a" | ||
|
||
# Work around issues where simulation is too slow to respond to keep-alive messages, | ||
# confusing either OpenOCD or GDB. Note that it will still complain about "missed" | ||
# deadlines, but it won't fail.. | ||
set remotetimeout unlimited | ||
|
||
# Connect to OpenOCD | ||
target extended-remote :3333 | ||
|
||
# List registers | ||
i r | ||
|
||
# Jump to start address, should output "a" | ||
jump *0x20000000 | ||
|
||
# Load program | ||
load "target/riscv32imc-unknown-none-elf/release/print_b" | ||
|
||
# Jump to start address. Should now output "b". | ||
jump *0x20000000 | ||
|
||
# Stop running GDB | ||
quit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
adapter driver jtag_tcp | ||
adapter speed 64000 | ||
transport select jtag | ||
|
||
|
||
set _ENDIAN little | ||
set _TAP_TYPE 1234 | ||
|
||
if { [info exists CPUTAPID] } { | ||
set _CPUTAPID $CPUTAPID | ||
} else { | ||
# set useful default | ||
set _CPUTAPID 0x10001fff | ||
} | ||
|
||
set _CHIPNAME vexrisc_ocd | ||
|
||
# The JTAG TAP itself is given the name "bridge", because it refers to the | ||
# JtagBridge that's part of the VexRiscv/SpinalHDL debug infrastructure. | ||
# In the example design, there is the JtagBridge controls a single CPU, but | ||
# the capability is there for 1 JTAG TAP + JtagBridge to control multiple | ||
# VexRiscv CPUs. | ||
jtag newtap $_CHIPNAME bridge -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0xF | ||
|
||
# There is 1 CPU controlled by the "bridge" JTAG TAP, "cpu0" | ||
target create $_CHIPNAME.cpu0 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.bridge | ||
|
||
# The JtagBridge/SystemDebugger receives commands in a serialized way. It gets synchronized into | ||
# a parallel bus, and a response is received. Along the way, there may be various clock domain | ||
# crossings or pipeline delays. | ||
# readWaitCycles instructs OpenOCD to insert idle JTAG clock cycles before shifting out | ||
# the response. | ||
# There aren't many transactions where read-back throughput is important, so there's little | ||
# points in lowballing this number. | ||
vexriscv readWaitCycles 10 | ||
|
||
# When the Verilog of a SpinalHDL design with one or more VexRiscv CPUs is created, the system | ||
# also creates a .yaml file with information that's sideband information that's important for | ||
# OpenOCD to control the CPU correctly. | ||
# A good example of this are the number of hardware breakpoints that are supported by the CPU. | ||
vexriscv cpuConfigFile clash-vexriscv/example-cpu/ExampleCpu.yaml | ||
|
||
# The rate at which OpenOCD polls active JTAG TAPs to check if there has been a notable | ||
# event. (E.g. to check if the CPU has hit a breakpoint.) | ||
# For some reason, making this number really low has an impact on the CPU while semihosting is | ||
# enabled? | ||
poll_period 50 | ||
|
||
# Initialize all JTAG TAPs and targets. | ||
init | ||
|
||
echo "Halting processor" | ||
|
||
# Halts the CPU and issue a soft reset. | ||
# The DebugPlugin has a resetOut signal that can be used reset external logic. It is not | ||
# used to reset anything inside the VexRiscv itself though. In our small example, | ||
# resetOut is not connected to anything, so we could have used "halt" instead. | ||
# soft_reset_halt | ||
halt | ||
|
||
sleep 1000 |