Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/common/util/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@ template <typename T> std::ostream& operator<<(std::ostream& stream, const std::
} // namespace logging
/** @} */
#undef LEVELS
#undef CAT

#ifndef NDEBUG
//! check only in debug mode
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(LIB_SOURCES
ocp/ocp_tlm.cpp
tilelink/tl_tlm.cpp
cxs/cxs_tlm.cpp
eth/eth_tlm.cpp
)

add_library(${PROJECT_NAME} ${LIB_SOURCES})
Expand Down
57 changes: 57 additions & 0 deletions src/interfaces/eth/eth_tlm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "eth_tlm.h"
namespace eth {
ethernet_frame ethernet_frame::parse_ethernet(nonstd::span<const std::uint8_t> frame) {
ethernet_frame eth_frame{};

if(frame.size() < sizeof(ethernet_header))
throw std::runtime_error("frame too small for Ethernet header");

// Copy header safely (no unaligned access)
std::memcpy(&eth_frame.l2, frame.data(), sizeof(ethernet_header));
std::uint16_t t = bswap16(eth_frame.l2.type_or_len);

std::size_t offset = sizeof(ethernet_header);

// VLAN TPIDs commonly: 0x8100 (802.1Q), 0x88A8 (802.1ad/QinQ)
auto is_vlan_tpid = [](std::uint16_t x) { return x == 0x8100 || x == 0x88A8; };

if(is_vlan_tpid(t)) {
if(frame.size() < offset + sizeof(vlan_tag_8021Q) + 2)
throw std::runtime_error("frame too small for VLAN tag");

vlan_tag_8021Q tag{};
std::memcpy(&tag, frame.data() + offset, sizeof(tag));
eth_frame.has_vlan = true;
eth_frame.vlan_tpid = t;

std::uint16_t tci = bswap16(tag.tci);
eth_frame.vlan_pcp = (tci >> 13) & 0x7;
eth_frame.vlan_dei = ((tci >> 12) & 0x1) != 0;
eth_frame.vlan_id = tci & 0x0FFF;

offset += sizeof(vlan_tag_8021Q);

// Next 2 bytes after VLAN tag is the *real* EtherType/Len
std::uint16_t inner{};
std::memcpy(&inner, frame.data() + offset, sizeof(inner));
t = bswap16(inner);
offset += sizeof(inner);
}

// Distinguish Ethernet II EtherType vs 802.3 length:
// <=1500 => length, >=1536 (0x0600) => EtherType
if(t <= 1500) {
eth_frame.kind = ether_kind::LENGTH_8023;
eth_frame.etherType_or_len = t;
} else {
eth_frame.kind = ether_kind::ETHER_TYPE;
eth_frame.etherType_or_len = t;
}

if(frame.size() < offset)
throw std::runtime_error("internal parse error (offset beyond frame)");

eth_frame.payload = frame.subspan(offset);
return eth_frame;
}
} // namespace eth
161 changes: 161 additions & 0 deletions src/interfaces/eth/eth_tlm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*******************************************************************************
* Copyright 2024 MINRES Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* 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.
*******************************************************************************/

#ifndef _ETH_ETH_TLM_H_
#define _ETH_ETH_TLM_H_

#include <cci_configuration>
#include <cstdint>
#include <nonstd/span.h>
#include <scc/fifo_w_cb.h>
#include <scc/peq.h>
#include <scc/report.h>
#include <scc/sc_variable.h>
#include <tlm/nw/tlm_network_gp.h>
#include <tlm/nw/tlm_network_sockets.h>
#include <tlm/scc/tlm_gp_shared.h>
#include <tlm/scc/tlm_mm.h>

//! @brief ETH TLM utilities
namespace eth {

inline std::uint16_t bswap16(std::uint16_t x) { return static_cast<std::uint16_t>((x >> 8) | (x << 8)); }

#pragma pack(push, 1)
struct ethernet_header {
std::array<std::uint8_t, 6> dst;
std::array<std::uint8_t, 6> src;
std::uint16_t type_or_len; // big-endian on the wire (e.g., 0x0800 IPv4, 0x86DD IPv6)
};

struct vlan_tag_8021Q {
std::uint16_t tpid; // 0x8100 (big-endian)
std::uint16_t tci; // PCP(3) | DEI(1) | VID(12) (big-endian)
};
#pragma pack(pop)

static_assert(sizeof(ethernet_header) == 14);
static_assert(sizeof(vlan_tag_8021Q) == 4);

enum class ether_kind { ETHER_TYPE, LENGTH_8023 };

struct ethernet_frame {
ethernet_header l2; // original 14-byte header
bool has_vlan = false;
std::uint16_t vlan_tpid = 0; // host order (e.g., 0x8100)
std::uint16_t vlan_id = 0; // 0..4095
std::uint16_t vlan_pcp = 0; // 0..7
bool vlan_dei = false;

ether_kind kind = ether_kind::ETHER_TYPE;
std::uint16_t etherType_or_len = 0; // host order: EtherType or 802.3 length
nonstd::span<const std::uint8_t> payload;
static ethernet_frame parse_ethernet(nonstd::span<const std::uint8_t> frame);
};

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
enum class ETH { FRAME };
struct eth_packet_payload : public tlm::nw::tlm_network_payload<ETH> {
eth_packet_payload() { m_data.resize(1536); };

explicit eth_packet_payload(tlm::nw::tlm_base_mm_interface* mm)
: tlm::nw::tlm_network_payload<ETH>(mm) {
m_data.resize(1536);
}

const sc_core::sc_time& get_sender_clk_period() const { return sender_clk_period; }

void set_sender_clk_period(sc_core::sc_time period) { this->sender_clk_period = period; }

private:
sc_core::sc_time sender_clk_period{sc_core::SC_ZERO_TIME};
};

struct eth_packet_types {
using tlm_payload_type = eth_packet_payload;
using tlm_phase_type = ::tlm::tlm_phase;
};

} // namespace eth

namespace tlm {
namespace scc {
// provide needed info for SCC memory manager
template <> struct tlm_mm_traits<eth::eth_packet_types> {
using mm_if_type = tlm::nw::tlm_base_mm_interface;
using payload_base = tlm::nw::tlm_network_payload_base;
};
} // namespace scc
} // namespace tlm

namespace eth {
template <int N = 1> using eth_pkt_initiator_socket = tlm::nw::tlm_network_initiator_socket<1, ETH, eth_packet_types, N>;
template <int N = 1> using eth_pkt_target_socket = tlm::nw::tlm_network_target_socket<1, ETH, eth_packet_types, N>;
using eth_pkt_shared_ptr = tlm::scc::tlm_payload_shared_ptr<eth_packet_payload>;
using eth_pkt_mm = tlm::scc::tlm_mm<eth_packet_types, false>;

struct eth_channel : public sc_core::sc_module,
public tlm::nw::tlm_network_fw_transport_if<eth_packet_types>,
public tlm::nw::tlm_network_bw_transport_if<eth_packet_types> {

using transaction_type = eth_packet_types::tlm_payload_type;
using phase_type = eth_packet_types::tlm_phase_type;

eth_pkt_target_socket<> tsck{"tsck"};

eth_pkt_initiator_socket<> isck{"isck"};

cci::cci_param<sc_core::sc_time> channel_delay{"channel_delay", sc_core::SC_ZERO_TIME, "delay of the SPI channel"};

eth_channel(sc_core::sc_module_name const& nm)
: sc_core::sc_module(nm)
, isck{"isckt"} {
isck(*this);
tsck(*this);
}

void b_transport(transaction_type& trans, sc_core::sc_time& t) override {
t += channel_delay;
isck->b_transport(trans, t);
}

tlm::tlm_sync_enum nb_transport_fw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) override {
SCCTRACE(SCMOD) << "Received non-blocking transaction in fw path with phase " << phase.get_name();
if(phase == tlm::nw::REQUEST) {
phase_type ph = tlm::nw::INDICATION;
auto ret = isck->nb_transport_fw(trans, ph, t);
if(ph == tlm::nw::RESPONSE)
phase = tlm::nw::CONFIRM;
return ret;
}
return tlm::TLM_ACCEPTED;
}

tlm::tlm_sync_enum nb_transport_bw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) override {
SCCTRACE(SCMOD) << "Received non-blocking transaction in bw path with phase " << phase.get_name();
if(phase == tlm::nw::RESPONSE) {
phase_type ph = tlm::nw::CONFIRM;
return tsck->nb_transport_bw(trans, ph, t);
}
return tlm::TLM_ACCEPTED;
}

unsigned int transport_dbg(transaction_type& trans) override { return isck->transport_dbg(trans); }
};
} // namespace eth
#endif // _ETH_ETH_TLM_H_