Skip to content

Commit bbd5af6

Browse files
committed
feat: add Verilator support to Bebop emulator with DMA handling and configuration updates
1 parent 51d6ad9 commit bbd5af6

File tree

8 files changed

+425
-4
lines changed

8 files changed

+425
-4
lines changed

bebop/bin/bebop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct Args {
2222
#[arg(long, value_name = "FILE")]
2323
trace_file: Option<String>,
2424

25-
/// Architecture type: buckyball or gemmini
25+
/// Architecture type: buckyball or gemmini or verilator-rtl
2626
#[arg(short, long, value_name = "ARCH")]
2727
arch: Option<String>,
2828

bebop/src/simulator/config/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ pub fn validate_config(config: &AppConfig) -> io::Result<()> {
284284

285285
// Validate arch_type is valid
286286
match config.simulation.arch_type.to_lowercase().as_str() {
287-
"buckyball" | "gemmini" => {},
287+
"buckyball" | "gemmini" | "verilator" | "verilator-rtl" => {},
288288
other => {
289289
return Err(io::Error::new(
290290
io::ErrorKind::InvalidData,

bebop/src/simulator/server/socket/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ pub mod cmd;
22
pub mod dma;
33
pub mod protocol;
44
pub mod server;
5+
pub mod verilator_client;
56

67
pub use cmd::CmdHandler;
78
pub use dma::{DmaReadHandler, DmaWriteHandler};
89
pub use protocol::*;
910
pub use server::accept_connection_async;
11+
pub use verilator_client::VerilatorClient;
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
use super::protocol::*;
2+
use std::io::{self, Read, Write, Result};
3+
use std::net::TcpStream;
4+
5+
// Verilator server ports (different from Bebop's 6000-6002)
6+
const VERILATOR_CMD_PORT: u16 = 7000;
7+
const VERILATOR_DMA_READ_PORT: u16 = 7001;
8+
const VERILATOR_DMA_WRITE_PORT: u16 = 7002;
9+
const VERILATOR_HOST: &str = "127.0.0.1";
10+
11+
pub struct VerilatorClient {
12+
cmd_stream: TcpStream,
13+
dma_read_stream: TcpStream,
14+
dma_write_stream: TcpStream,
15+
}
16+
17+
impl VerilatorClient {
18+
pub fn connect() -> Result<Self> {
19+
eprintln!("[VerilatorClient] Connecting to Verilator server...");
20+
21+
// Connect to CMD port
22+
let cmd_stream = TcpStream::connect(format!("{}:{}", VERILATOR_HOST, VERILATOR_CMD_PORT))
23+
.map_err(|e| {
24+
io::Error::new(
25+
io::ErrorKind::ConnectionRefused,
26+
format!("Failed to connect to Verilator CMD port {}: {}", VERILATOR_CMD_PORT, e),
27+
)
28+
})?;
29+
eprintln!("[VerilatorClient] Connected to CMD port {}", VERILATOR_CMD_PORT);
30+
31+
// Connect to DMA Read port
32+
let dma_read_stream = TcpStream::connect(format!("{}:{}", VERILATOR_HOST, VERILATOR_DMA_READ_PORT))
33+
.map_err(|e| {
34+
io::Error::new(
35+
io::ErrorKind::ConnectionRefused,
36+
format!("Failed to connect to Verilator DMA Read port {}: {}", VERILATOR_DMA_READ_PORT, e),
37+
)
38+
})?;
39+
eprintln!("[VerilatorClient] Connected to DMA Read port {}", VERILATOR_DMA_READ_PORT);
40+
41+
// Connect to DMA Write port
42+
let dma_write_stream = TcpStream::connect(format!("{}:{}", VERILATOR_HOST, VERILATOR_DMA_WRITE_PORT))
43+
.map_err(|e| {
44+
io::Error::new(
45+
io::ErrorKind::ConnectionRefused,
46+
format!("Failed to connect to Verilator DMA Write port {}: {}", VERILATOR_DMA_WRITE_PORT, e),
47+
)
48+
})?;
49+
eprintln!("[VerilatorClient] Connected to DMA Write port {}", VERILATOR_DMA_WRITE_PORT);
50+
51+
Ok(Self {
52+
cmd_stream,
53+
dma_read_stream,
54+
dma_write_stream,
55+
})
56+
}
57+
58+
// Send CMD request and receive response
59+
pub fn send_cmd(&mut self, funct: u32, xs1: u64, xs2: u64) -> Result<u64> {
60+
// Send CMD request
61+
let req = CmdReq {
62+
header: MsgHeader {
63+
msg_type: MsgType::CmdReq as u32,
64+
reserved: 0,
65+
},
66+
funct,
67+
padding: 0,
68+
xs1,
69+
xs2,
70+
};
71+
72+
write_struct(&mut self.cmd_stream, &req)?;
73+
self.cmd_stream.flush()?;
74+
75+
// Receive CMD response
76+
let resp: CmdResp = read_struct(&mut self.cmd_stream)?;
77+
78+
Ok(resp.result)
79+
}
80+
81+
// Handle DMA read request from Verilator
82+
pub fn handle_dma_read_request<F>(&mut self, read_cb: F) -> Result<()>
83+
where
84+
F: Fn(u64, u32) -> (u64, u64), // (addr, size) -> (data_lo, data_hi)
85+
{
86+
// Receive DMA read request
87+
let req: DmaReadReq = read_struct(&mut self.dma_read_stream)?;
88+
89+
// Call callback to read from memory
90+
let (data_lo, data_hi) = read_cb(req.addr, req.size);
91+
92+
// Send DMA read response
93+
let resp = DmaReadResp {
94+
header: MsgHeader {
95+
msg_type: MsgType::DmaReadResp as u32,
96+
reserved: 0,
97+
},
98+
data_lo,
99+
data_hi,
100+
};
101+
102+
write_struct(&mut self.dma_read_stream, &resp)?;
103+
self.dma_read_stream.flush()?;
104+
105+
Ok(())
106+
}
107+
108+
// Handle DMA write request from Verilator
109+
pub fn handle_dma_write_request<F>(&mut self, write_cb: F) -> Result<()>
110+
where
111+
F: Fn(u64, u64, u64, u32), // (addr, data_lo, data_hi, size)
112+
{
113+
// Receive DMA write request
114+
let req: DmaWriteReq = read_struct(&mut self.dma_write_stream)?;
115+
116+
// Call callback to write to memory
117+
write_cb(req.addr, req.data_lo, req.data_hi, req.size);
118+
119+
// Send DMA write response
120+
let resp = DmaWriteResp {
121+
header: MsgHeader {
122+
msg_type: MsgType::DmaWriteResp as u32,
123+
reserved: 0,
124+
},
125+
reserved: 0,
126+
};
127+
128+
write_struct(&mut self.dma_write_stream, &resp)?;
129+
self.dma_write_stream.flush()?;
130+
131+
Ok(())
132+
}
133+
134+
// Blocking receive DMA read request
135+
pub fn recv_dma_read_request(&mut self) -> Result<DmaReadReq> {
136+
read_struct(&mut self.dma_read_stream)
137+
}
138+
139+
// Blocking receive DMA write request
140+
pub fn recv_dma_write_request(&mut self) -> Result<DmaWriteReq> {
141+
read_struct(&mut self.dma_write_stream)
142+
}
143+
144+
// Try to receive DMA read request (non-blocking)
145+
pub fn try_recv_dma_read_request(&mut self) -> Result<Option<DmaReadReq>> {
146+
self.dma_read_stream.set_nonblocking(true)?;
147+
148+
let result = match read_struct::<DmaReadReq>(&mut self.dma_read_stream) {
149+
Ok(req) => Ok(Some(req)),
150+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
151+
Err(e) => Err(e),
152+
};
153+
154+
self.dma_read_stream.set_nonblocking(false)?;
155+
result
156+
}
157+
158+
// Try to receive DMA write request (non-blocking)
159+
pub fn try_recv_dma_write_request(&mut self) -> Result<Option<DmaWriteReq>> {
160+
self.dma_write_stream.set_nonblocking(true)?;
161+
162+
let result = match read_struct::<DmaWriteReq>(&mut self.dma_write_stream) {
163+
Ok(req) => Ok(Some(req)),
164+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
165+
Err(e) => Err(e),
166+
};
167+
168+
self.dma_write_stream.set_nonblocking(false)?;
169+
result
170+
}
171+
172+
pub fn send_dma_read_response(&mut self, data_lo: u64, data_hi: u64) -> Result<()> {
173+
let resp = DmaReadResp {
174+
header: MsgHeader {
175+
msg_type: MsgType::DmaReadResp as u32,
176+
reserved: 0,
177+
},
178+
data_lo,
179+
data_hi,
180+
};
181+
182+
write_struct(&mut self.dma_read_stream, &resp)?;
183+
self.dma_read_stream.flush()?;
184+
Ok(())
185+
}
186+
187+
pub fn send_dma_write_response(&mut self) -> Result<()> {
188+
let resp = DmaWriteResp {
189+
header: MsgHeader {
190+
msg_type: MsgType::DmaWriteResp as u32,
191+
reserved: 0,
192+
},
193+
reserved: 0,
194+
};
195+
196+
write_struct(&mut self.dma_write_stream, &resp)?;
197+
self.dma_write_stream.flush()?;
198+
Ok(())
199+
}
200+
}

bebop/src/simulator/sim/mode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub enum StepMode {
88
pub enum ArchType {
99
Buckyball,
1010
Gemmini,
11+
VerilatorRTL,
1112
}
1213

1314
#[derive(Debug, Clone, PartialEq, Eq)]

0 commit comments

Comments
 (0)