Skip to content

Commit

Permalink
Add smoltcp example tcp server
Browse files Browse the repository at this point in the history
  • Loading branch information
lmbollen committed Jul 15, 2024
1 parent 0f4f3b0 commit 3d5e08b
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 0 deletions.
13 changes: 13 additions & 0 deletions firmware-binaries/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions firmware-binaries/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ codegen-units = 1
[workspace]
members = [
"examples/hello",
"examples/smoltcp_echo",

"test-cases/time_self_test",
"test-cases/smoltcp_loopback",
"test-cases/axi_stream_self_test",
Expand Down
21 changes: 21 additions & 0 deletions firmware-binaries/examples/smoltcp_echo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: 2024 Google LLC
#
# SPDX-License-Identifier: CC0-1.0

[package]
name = "smoltcp_echo"
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]
bittide-sys = { path = "../../../firmware-support/bittide-sys" }
riscv-rt = "0.11.0"
riscv = "^0.10"
heapless = { version = "0.8", default-features = false}
smoltcp = {default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-tcp"] }
ufmt = "0.2.0"
log = {version = "0.4.21", features = ["release_max_level_trace"]}
23 changes: 23 additions & 0 deletions firmware-binaries/examples/smoltcp_echo/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2024 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");
}
18 changes: 18 additions & 0 deletions firmware-binaries/examples/smoltcp_echo/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
SPDX-FileCopyrightText: 2024 Google LLC
SPDX-License-Identifier: CC0-1.0
*/

MEMORY
{
IMEM : ORIGIN = 0x80000000, LENGTH = 256K
DMEM : ORIGIN = 0x20000000, LENGTH = 64K
}

REGION_ALIAS("REGION_TEXT", IMEM);
REGION_ALIAS("REGION_RODATA", DMEM);
REGION_ALIAS("REGION_DATA", DMEM);
REGION_ALIAS("REGION_BSS", DMEM);
REGION_ALIAS("REGION_HEAP", DMEM);
REGION_ALIAS("REGION_STACK", DMEM);
7 changes: 7 additions & 0 deletions firmware-binaries/examples/smoltcp_echo/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2024 Google LLC
#
# SPDX-License-Identifier: CC0-1.0

[toolchain]
channel = "stable"
targets = [ "riscv32imc-unknown-none-elf" ]
136 changes: 136 additions & 0 deletions firmware-binaries/examples/smoltcp_echo/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-FileCopyrightText: 2024 Google LLC
//
// SPDX-License-Identifier: Apache-2.0

#![cfg_attr(not(feature = "std"), no_std)]
#![allow(unused_mut)]
#![allow(clippy::collapsible_if)]
#![no_main]

use bittide_sys::axi::{AxiRx, AxiTx};
use bittide_sys::smoltcp::axi::AxiEthernet;
use bittide_sys::time::{Clock, Duration};
use bittide_sys::uart::Uart;
use core::fmt::Write;
use log::{self, debug};
#[cfg(not(test))]
use riscv_rt::entry;

use smoltcp::iface::{Config, Interface, SocketSet};
use smoltcp::phy::Medium;
use smoltcp::socket::tcp::{Socket, SocketBuffer};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use ufmt::uwriteln;

const UART_ADDR: usize = 0b010 << 29;
const CLOCK_ADDR: usize = 0b011 << 29;
const RX_AXI_ADDR: usize = 0b101 << 29;
const TX_AXI_ADDR: usize = 0b110 << 29;
const RX_BUFFER_SIZE: usize = 2048;

#[cfg_attr(not(test), entry)]
fn main() -> ! {
// Initialize and test UART.
let mut uart = unsafe { Uart::new(UART_ADDR as *mut u8) };

uwriteln!(uart, "Starting smoltcp-echo").unwrap();
// Initialize and test clock
let mut clock = unsafe { Clock::new(CLOCK_ADDR as *const u32) };

// Create interface
let eth_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let mut config = Config::new(eth_addr.into());
let axi_tx = AxiTx::new(TX_AXI_ADDR as *mut u8);
let axi_rx = unsafe { AxiRx::new(RX_AXI_ADDR as *mut usize, RX_BUFFER_SIZE) };

let mut device: AxiEthernet<2048> = AxiEthernet::new(Medium::Ethernet, axi_rx, axi_tx);
// let mut device = Loopback::new(Medium::Ethernet, UART_ADDR as *mut u8);
let now = clock.elapsed().into();
let mut iface = Interface::new(config, &mut device, now);
iface.update_ip_addrs(|ip_addrs| {
ip_addrs
.push(IpCidr::new(IpAddress::v4(100, 100, 100, 100), 8))
.unwrap();
});

// Create sockets
let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but
// on embedded systems that smoltcp targets it is far better to allocate the data
// statically to verify that it fits into RAM rather than get undefined behavior
// when stack overflows.
static mut TCP_SERVER_RX_DATA: [u8; 2048] = [0; 2048];
static mut TCP_SERVER_TX_DATA: [u8; 2048] = [0; 2048];
let tcp_rx_buffer = SocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] });
let tcp_tx_buffer = SocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] });
Socket::new(tcp_rx_buffer, tcp_tx_buffer)
};

let mut sockets: [_; 1] = Default::default();
let mut sockets = SocketSet::new(&mut sockets[..]);
let server_handle = sockets.add(server_socket);

let mut last_print = clock.elapsed();
loop {
let now = clock.elapsed();
if now > last_print + Duration::from_secs(1) {
last_print = now;
uwriteln!(uart, "time: {}", now).unwrap();
let socket = sockets.get::<Socket>(server_handle);
writeln!(uart, "socket: {:?}", socket.state()).unwrap();
}
let elapsed = clock.elapsed().into();
iface.poll(elapsed, &mut device, &mut sockets);

let mut socket = sockets.get_mut::<Socket>(server_handle);
if !socket.is_active() && !socket.is_listening() {
uwriteln!(uart, "listening").unwrap();
socket.listen(7).unwrap();
}
if socket.is_open() && socket.may_send() && !socket.may_recv() {
uwriteln!(uart, "Closing socket").unwrap();
socket.close();
}
if socket.can_recv() {
let mut buf = [0; 2048];
let mut len = 0;
socket
.recv(|buffer| {
len = buffer.len();
buf[..len].copy_from_slice(buffer);
(buffer.len(), ())
})
.unwrap();
socket.send_slice(&buf[0..len]).unwrap();
}

match iface.poll_delay(clock.elapsed().into(), &sockets) {
Some(smoltcp::time::Duration::ZERO) => {}
Some(delay) => {
debug!("sleeping for {} ms", delay);
clock.wait(Duration::from(delay));
}
None => clock.wait(Duration::from_millis(1)),
}
}
}

#[export_name = "ExceptionHandler"]
fn exception_handler(_trap_frame: &riscv_rt::TrapFrame) -> ! {
let mut uart = unsafe { Uart::new(UART_ADDR as *mut u8) };
riscv::interrupt::free(|| {
uwriteln!(uart, "... caught an exception. Looping forever now.\n").unwrap();
});
loop {
continue;
}
}

#[panic_handler]
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
let mut uart = unsafe { Uart::new(UART_ADDR as *mut u8) };
uwriteln!(uart, "Panicked, looping forever now").unwrap();
loop {
continue;
}
}

0 comments on commit 3d5e08b

Please sign in to comment.