zynq: zero-copy implementation of nic_drv

- Packet stream buffers are directly passed to DMA.
- Also enables pause frames and checksum offloading.

Issue #3053
This commit is contained in:
Johannes Schlatow 2018-09-29 12:31:49 +02:00 committed by Christian Helmuth
parent 8ad56a6c0e
commit dc0bfd7008
5 changed files with 394 additions and 206 deletions

View File

@ -1,11 +1,12 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
* Copyright (C) 2015-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -25,15 +26,12 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
{
public:
static const size_t BUFFER_DESC_SIZE = 0x08;
static const size_t MAX_PACKAGE_SIZE = 1600;
static const size_t BUFFER_SIZE = BUFFER_DESC_SIZE + MAX_PACKAGE_SIZE;
static const size_t BUFFER_SIZE = 1600;
private:
const size_t _buffer_count;
const size_t _buffer_offset;
unsigned int _descriptor_index;
char* const _buffer;
size_t _buffer_count;
size_t _head_idx { 0 };
size_t _tail_idx { 0 };
protected:
typedef struct {
@ -43,27 +41,49 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
descriptor_t* const _descriptors;
/* set the maximum descriptor index */
inline
void _max_index(size_t max_index) { _buffer_count = max_index+1; };
/* get the maximum descriptor index */
inline
unsigned _max_index() { return _buffer_count-1; }
void _increment_descriptor_index()
inline
void _advance_head()
{
_descriptor_index++;
_descriptor_index %= _buffer_count;
_head_idx = (_head_idx+1) % _buffer_count;
}
descriptor_t& _current_descriptor()
inline
void _advance_tail()
{
return _descriptors[_descriptor_index];
_tail_idx = (_tail_idx+1) % (_buffer_count);
}
char * _current_buffer()
inline
descriptor_t& _head()
{
char * const buffer = &_buffer[MAX_PACKAGE_SIZE * _descriptor_index];
return buffer;
return _descriptors[_head_idx];
}
inline
descriptor_t& _tail()
{
return _descriptors[_tail_idx];
}
size_t _queued() const
{
if (_head_idx >= _tail_idx)
return _head_idx - _tail_idx;
else
return _head_idx + _buffer_count - _tail_idx;
}
size_t _head_index() const { return _head_idx; }
size_t _tail_index() const { return _tail_idx; }
private:
/*
@ -72,6 +92,7 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
Buffer_descriptor(Buffer_descriptor const &);
Buffer_descriptor &operator = (Buffer_descriptor const &);
public:
/*
* start of the ram spave contains all buffer descriptors
@ -79,33 +100,13 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
*/
Buffer_descriptor(Genode::Env &env, const size_t buffer_count = 1)
:
Attached_ram_dataspace(env.ram(), env.rm(), BUFFER_SIZE * buffer_count, UNCACHED),
Attached_ram_dataspace(env.ram(), env.rm(), BUFFER_DESC_SIZE * buffer_count, UNCACHED),
Genode::Mmio( reinterpret_cast<addr_t>(local_addr<void>()) ),
_buffer_count(buffer_count),
_buffer_offset(BUFFER_DESC_SIZE * buffer_count),
_descriptor_index(0),
_buffer(local_addr<char>() + _buffer_offset),
_descriptors(local_addr<descriptor_t>())
{
/*
* Save address of _buffer.
* Has to be the physical address and not the virtual addresse,
* because the dma controller of the nic will use it.
* Ignore lower two bits,
* because this are status bits.
*/
for (unsigned int i=0; i<buffer_count; i++) {
_descriptors[i].addr = (phys_addr_buffer(i)) & ~0x3;
}
}
{ }
addr_t phys_addr() { return Dataspace_client(cap()).phys_addr(); }
addr_t phys_addr_buffer(const unsigned int index)
{
return phys_addr() + _buffer_offset + MAX_PACKAGE_SIZE * index;
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_ */

View File

@ -1,12 +1,12 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Johannes Schlatow
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
* Copyright (C) 2015-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -52,6 +52,7 @@ namespace Genode
struct Mgmt_port_en : Bitfield<4, 1> {};
struct Clear_statistics : Bitfield<5, 1> {};
struct Start_tx : Bitfield<9, 1> {};
struct Tx_pause : Bitfield<11, 1> {};
static access_t init() {
return Mgmt_port_en::bits(1) |
@ -75,6 +76,7 @@ namespace Genode
struct No_broadcast : Bitfield<5, 1> {};
struct Multi_hash_en : Bitfield<6, 1> {};
struct Gige_en : Bitfield<10, 1> {};
struct Pause_en : Bitfield<13, 1> {};
struct Fcs_remove : Bitfield<17, 1> {};
struct Mdc_clk_div : Bitfield<18, 3> {
enum {
@ -82,6 +84,8 @@ namespace Genode
DIV_224 = 0b111,
};
};
struct Dis_cp_pause : Bitfield<23, 1> {};
struct Rx_chksum_en : Bitfield<24, 1> {};
struct Ignore_rx_fcs : Bitfield<26, 1> {};
};
@ -100,6 +104,7 @@ namespace Genode
*/
struct Dma_config : Register<0x10, 32>
{
struct Disc_when_no_ahb : Bitfield<24,1> {};
struct Rx_pktbuf_memsz_sel : Bitfield<8, 2> {
enum {
SPACE_8KB = 0x3,
@ -115,15 +120,25 @@ namespace Genode
BUFFER_1600B = 0x19,
};
};
struct Csum_gen_en : Bitfield<11, 1> { };
struct Burst_len : Bitfield<0, 5> {
enum {
INCR16 = 0x10,
INCR8 = 0x08,
INCR4 = 0x04,
SINGLE = 0x01
};
};
static access_t init()
{
return Ahb_mem_rx_buf_size::bits(Ahb_mem_rx_buf_size::BUFFER_1600B) |
Rx_pktbuf_memsz_sel::bits(Rx_pktbuf_memsz_sel::SPACE_8KB) |
Tx_pktbuf_memsz_sel::bits(Tx_pktbuf_memsz_sel::SPACE_4KB);
Tx_pktbuf_memsz_sel::bits(Tx_pktbuf_memsz_sel::SPACE_4KB) |
Disc_when_no_ahb::bits(1) |
Csum_gen_en::bits(1) |
Burst_len::bits(Burst_len::INCR16);
}
// TODO possibly enable transmition check sum offloading
};
/**
@ -151,36 +166,40 @@ namespace Genode
struct Addr : Bitfield<0, 32> {};
};
/**
* Receive status register
*/
struct Rx_status : Register<0x20, 32>
{
struct Frame_reveived : Bitfield<1, 1> {};
struct Rx_overrun : Bitfield<2, 1> {};
struct Frame_received : Bitfield<1, 1> {};
struct Buffer_not_available : Bitfield<0, 1> {};
};
/**
* Interrupt status register
*/
struct Interrupt_status : Register<0x24, 32>
{
struct Rx_used_read : Bitfield<3, 1> {};
struct Rx_complete : Bitfield<1, 1> {};
struct Rx_used_read : Bitfield<2, 1> {};
struct Rx_complete : Bitfield<1, 1> {};
struct Pause_zero : Bitfield<13,1> {};
struct Pause_received : Bitfield<12,1> {};
struct Rx_overrun : Bitfield<10,1> {};
};
/**
* Interrupt enable register
*/
struct Interrupt_enable : Register<0x28, 32>
{
struct Rx_complete : Bitfield<1, 1> {};
struct Rx_used_read : Bitfield<2, 1> {};
struct Rx_complete : Bitfield<1, 1> {};
struct Pause_zero : Bitfield<13,1> {};
struct Pause_received : Bitfield<12,1> {};
struct Rx_overrun : Bitfield<10,1> {};
};
/**
* Interrupt disable register
*/
@ -189,7 +208,6 @@ namespace Genode
struct Rx_complete : Bitfield<1, 1> {};
};
/**
* PHY maintenance register
*/
@ -210,7 +228,6 @@ namespace Genode
struct Data : Bitfield<0, 16> {};
};
/**
* MAC hash register
*/
@ -220,7 +237,6 @@ namespace Genode
struct High_hash : Bitfield<32, 16> { };
};
/**
* MAC Addresse
*/
@ -230,7 +246,6 @@ namespace Genode
struct High_addr : Bitfield<32, 16> { };
};
/**
* Counter for the successfully transmitted frames
*/
@ -239,6 +254,13 @@ namespace Genode
struct Counter : Bitfield<0, 32> { };
};
/**
* Counter for the transmitted pause frames
*/
struct Pause_transmitted : Register<0x114, 32>
{
struct Counter : Bitfield<0, 16> { };
};
/**
* Counter for the successfully received frames
@ -248,15 +270,62 @@ namespace Genode
struct Counter : Bitfield<0, 32> { };
};
/**
* Counter for resource error statistics
*/
struct Rx_resource_errors : Register<0x1A0, 32>
{
struct Counter : Bitfield<0, 18> { };
};
/**
* Counter for the successfully received frames
* Counter for overrun statistics
*/
struct Rx_overrun_errors : Register<0x1A4, 32>
{
struct Counter : Bitfield<0, 10> { };
};
/**
* Counter for IP checksum errors
*/
struct Rx_ip_chksum_errors : Register<0x1A8, 32>
{
struct Counter : Bitfield<0, 8> { };
};
/**
* Counter for TCP checksum errors
*/
struct Rx_tcp_chksum_errors : Register<0x1AC, 32>
{
struct Counter : Bitfield<0, 8> { };
};
/**
* Counter for UDP checksum errors
*/
struct Rx_udp_chksum_errors : Register<0x1B0, 32>
{
struct Counter : Bitfield<0, 8> { };
};
/**
* Counter for FCS errors
*/
struct Rx_fcs_errors : Register<0x190, 32>
{
struct Counter : Bitfield<0, 10> { };
};
/**
* Counter for pause frames received
*/
struct Pause_received : Register<0x164, 32>
{
struct Counter : Bitfield<0, 16> { };
};
class Phy_timeout_for_idle : public Genode::Exception {};
class Unkown_ethernet_speed : public Genode::Exception {};
@ -278,10 +347,14 @@ namespace Genode
/* 1. Program the Network Configuration register (gem.net_cfg) */
write<Config>(
Config::Gige_en::bits(1) |
Config::Speed_100::bits(1) |
Config::Pause_en::bits(1) |
Config::Full_duplex::bits(1) |
Config::Multi_hash_en::bits(1) |
Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
Config::Dis_cp_pause::bits(1) |
Config::Rx_chksum_en::bits(1) |
Config::Fcs_remove::bits(1)
);
@ -312,12 +385,14 @@ namespace Genode
write<Config::Gige_en>(1);
rclk = (0 << 4) | (1 << 0);
clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
log("Autonegotiation result: 1Gbit/s");
break;
case SPEED_100:
write<Config::Gige_en>(0);
write<Config::Speed_100>(1);
rclk = 1 << 0;
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
log("Autonegotiation result: 100Mbit/s");
break;
case SPEED_10:
write<Config::Gige_en>(0);
@ -325,6 +400,7 @@ namespace Genode
rclk = 1 << 0;
/* FIXME untested */
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
log("Autonegotiation result: 10Mbit/s");
break;
default:
throw Unkown_ethernet_speed();
@ -333,7 +409,11 @@ namespace Genode
/* 16.3.6 Configure Interrupts */
write<Interrupt_enable>(Interrupt_enable::Rx_complete::bits(1));
write<Interrupt_enable>(Interrupt_enable::Rx_complete::bits(1) |
Interrupt_enable::Rx_overrun::bits(1) |
Interrupt_enable::Pause_received::bits(1) |
Interrupt_enable::Pause_zero::bits(1) |
Interrupt_enable::Rx_used_read::bits(1));
}
void _deinit()
@ -394,6 +474,14 @@ namespace Genode
_mdio_wait();
}
inline void _handle_acks()
{
while (_rx.source()->ack_avail()) {
Nic::Packet_descriptor p = _rx.source()->get_acked_packet();
_rx_buffer.reset_descriptor(p);
}
}
virtual void _handle_irq()
{
/* 16.3.9 Receiving Frames */
@ -401,51 +489,82 @@ namespace Genode
const Interrupt_status::access_t status = read<Interrupt_status>();
const Rx_status::access_t rxStatus = read<Rx_status>();
/* FIXME strangely, this handler is also called without any status bit set in Interrupt_status */
if ( Interrupt_status::Rx_complete::get(status) ) {
while (_rx_buffer.package_available()) {
// TODO use this buffer directly as the destination for the DMA controller
// to minimize the overrun errors
const size_t buffer_size = _rx_buffer.package_length();
while (_rx_buffer.next_packet()) {
/* allocate rx packet buffer */
Nic::Packet_descriptor p;
try {
p = _rx.source()->alloc_packet(buffer_size);
} catch (Session::Rx::Source::Packet_alloc_failed) { return; }
_handle_acks();
char *dst = (char *)_rx.source()->packet_content(p);
/*
* copy data from rx buffer to new allocated buffer.
* Has to be copied,
* because the extern allocater possibly is using the cache.
*/
if ( _rx_buffer.get_package(dst, buffer_size) != buffer_size ) {
PWRN("Package not fully copiied. Package ignored.");
break;
}
/* clearing error flags */
write<Interrupt_status::Rx_used_read>(1);
write<Rx_status::Buffer_not_available>(1);
/* comit buffer to system services */
_rx.source()->submit_packet(p);
Nic::Packet_descriptor p = _rx_buffer.get_packet_descriptor();
if (_rx.source()->packet_valid(p))
_rx.source()->submit_packet(p);
else
Genode::error("invalid packet descriptor ", Genode::Hex(p.offset()),
" size ", Genode::Hex(p.size()));
}
/* check, if there was lost some packages */
const uint16_t lost_packages = read<Rx_overrun_errors::Counter>();
if (lost_packages > 0) {
PWRN("%d packages lost (%d packages successfully received)!",
lost_packages, read<Frames_received>());
}
/* reset reveive complete interrupt */
write<Rx_status>(Rx_status::Frame_reveived::bits(1));
/* reset receive complete interrupt */
write<Rx_status>(Rx_status::Frame_received::bits(1));
write<Interrupt_status>(Interrupt_status::Rx_complete::bits(1));
}
else {
_handle_acks();
}
bool print_stats = false;
if (Interrupt_status::Rx_overrun::get(status)) {
write<Control::Tx_pause>(1);
write<Interrupt_status>(Interrupt_status::Rx_overrun::bits(1));
write<Rx_status>(Rx_status::Rx_overrun::bits(1));
print_stats = true;
Genode::error("Rx overrun");
}
if (Interrupt_status::Rx_used_read::get(status)) {
/* tried to use buffer descriptor with used bit set */
/* we sent a pause frame because the buffer appears to
* be full
*/
write<Control::Tx_pause>(1);
write<Interrupt_status>(Interrupt_status::Rx_used_read::bits(1));
write<Rx_status>(Rx_status::Buffer_not_available::bits(1));
print_stats = true;
Genode::error("Rx used");
}
if (Interrupt_status::Pause_zero::get(status)) {
Genode::warning("Pause ended.");
write<Interrupt_status>(Interrupt_status::Pause_zero::bits(1));
print_stats = true;
}
if (Interrupt_status::Pause_received::get(status)) {
Genode::warning("Pause frame received.");
write<Interrupt_status>(Interrupt_status::Pause_received::bits(1));
print_stats = true;
}
if (print_stats) {
/* check, if there was lost some packages */
const uint32_t received = read<Frames_received>();
const uint32_t pause_rx = read<Pause_received::Counter>();
const uint32_t res_err = read<Rx_resource_errors::Counter>();
const uint32_t overrun = read<Rx_overrun_errors::Counter>();
const uint32_t fcs_err = read<Rx_fcs_errors::Counter>();
const uint32_t ip_chk = read<Rx_ip_chksum_errors::Counter>();
const uint32_t udp_chk = read<Rx_udp_chksum_errors::Counter>();
const uint32_t tcp_chk = read<Rx_tcp_chksum_errors::Counter>();
const uint32_t transmit = read<Frames_transmitted>();
const uint32_t pause_tx = read<Pause_transmitted::Counter>();
Genode::warning("Received: ", received);
Genode::warning(" pause frames: ", pause_rx);
Genode::warning(" resource errors: ", res_err);
Genode::warning(" overrun errors: ", overrun);
Genode::warning(" FCS errors: ", fcs_err);
Genode::warning(" IP chk failed: ", ip_chk);
Genode::warning(" UDP chk failed: ", udp_chk);
Genode::warning(" TCP chk failed: ", tcp_chk);
Genode::warning("Transmitted: ", transmit);
Genode::warning(" pause frames: ", pause_tx);
}
_irq.ack_irq();
}
@ -465,7 +584,9 @@ namespace Genode
Genode::Attached_mmio(env, base, size),
Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, env),
_timer(env),
_sys_ctrl(env, _timer), _tx_buffer(env, _timer), _rx_buffer(env),
_sys_ctrl(env, _timer),
_tx_buffer(env, *_tx.sink(), _timer),
_rx_buffer(env, *_rx.source()),
_irq(env, irq),
_irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq),
_phy(*this, _timer)
@ -511,6 +632,10 @@ namespace Genode
bool _send()
{
/* first, see whether we can acknowledge any
* previously sent packet */
_tx_buffer.submit_acks(*_tx.sink());
if (!_tx.sink()->ready_to_ack())
return false;
@ -523,12 +648,14 @@ namespace Genode
return true;
}
char *src = (char *)_tx.sink()->packet_content(packet);
try {
_tx_buffer.add_to_queue(packet);
write<Control>(Control::start_tx());
} catch (Tx_buffer_descriptor::Package_send_timeout) {
Genode::warning("Package Tx timeout");
return false;
}
_tx_buffer.add_to_queue(src, packet.size());
write<Control>(Control::start_tx());
_tx.sink()->acknowledge_packet(packet);
return true;
}
@ -557,10 +684,9 @@ namespace Genode
void _handle_packet_stream() override
{
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
_handle_acks();
while (_send()) ;
while (_send());
}
};
}

View File

@ -59,7 +59,6 @@ class Server::Gem_session_component : public Cadence_gem
try {
Genode::Xml_node nic_config = _config_rom.xml().sub_node("nic");
nic_config.attribute("mac").value(&mac_addr);
Genode::log("Using configured MAC address ", mac_addr);
} catch (...) {
/* fall back to fake MAC address (unicast, locally managed) */
mac_addr.addr[0] = 0x02;
@ -70,6 +69,8 @@ class Server::Gem_session_component : public Cadence_gem
mac_addr.addr[5] = 0x01;
}
Genode::log("Using MAC address ", mac_addr);
/* set mac address */
mac_address(mac_addr);
}

View File

@ -1,11 +1,12 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
* Copyright (C) 2015-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -22,10 +23,11 @@ using namespace Genode;
class Rx_buffer_descriptor : public Buffer_descriptor
{
private:
struct Addr : Register<0x00, 32> {
struct Addr31to2 : Bitfield<2, 28> {};
struct Addr31to2 : Bitfield<2, 30> {};
struct Wrap : Bitfield<1, 1> {};
struct Package_available : Bitfield<0, 1> {};
struct Used : Bitfield<0, 1> {};
};
struct Status : Register<0x04, 32> {
struct Length : Bitfield<0, 13> {};
@ -33,102 +35,101 @@ class Rx_buffer_descriptor : public Buffer_descriptor
struct End_of_frame : Bitfield<15, 1> {};
};
enum { BUFFER_COUNT = 16 };
enum { MAX_BUFFER_COUNT = 1024 };
addr_t const _phys_base;
/**
* @brief _set_buffer_processed resets the available flag
* So the DMA controller can use this buffer for an received package.
* The buffer index will be incremented, too.
* So the right sequenz of packages will be keeped.
*/
void _set_package_processed()
{
/* reset package available for new package */
_current_descriptor().addr &= ~Addr::Package_available::bits(1);
/* use next buffer descriptor for next package */
_increment_descriptor_index();
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
if (i > _max_index())
return;
/* clear status */
_descriptors[i].status = 0;
/* set physical buffer address and set not used by SW
* last descriptor must be marked by Wrap bit
*/
_descriptors[i].addr =
(phys_addr & Addr::Addr31to2::reg_mask())
| Addr::Wrap::bits(i == _max_index());
}
inline bool _head_available()
{
return Addr::Used::get(_head().addr)
&& Status::Length::get(_head().status);
}
public:
Rx_buffer_descriptor(Genode::Env &env) : Buffer_descriptor(env, BUFFER_COUNT)
Rx_buffer_descriptor(Genode::Env &env,
Nic::Session::Tx::Source &source)
: Buffer_descriptor(env, MAX_BUFFER_COUNT),
_phys_base(Dataspace_client(source.dataspace()).phys_addr())
{
/*
* mark the last buffer descriptor
* so the dma will start at the beginning again
*/
_descriptors[BUFFER_COUNT-1].addr |= Addr::Wrap::bits(1);
for (size_t i=0; i <= _max_index(); i++) {
try {
Nic::Packet_descriptor p = source.alloc_packet(BUFFER_SIZE);
_reset_descriptor(i, _phys_base + p.offset());
} catch (Nic::Session::Rx::Source::Packet_alloc_failed) {
/* set new _buffer_count */
_max_index(i-1);
/* set wrap bit */
_descriptors[_max_index()].addr |= Addr::Wrap::bits(1);
break;
}
}
Genode::log("Initialised ", _max_index()+1, " RX buffer descriptors");
}
bool package_available()
bool reset_descriptor(Packet_descriptor pd)
{
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
const bool available = Addr::Package_available::get(_current_descriptor().addr);
if (available) {
addr_t const phys = _phys_base + pd.offset();
for (size_t i=0; i <= _max_index(); i++) {
_advance_tail();
if (Addr::Addr31to2::masked(_tail().addr) == phys) {
_reset_descriptor(_tail_index(), phys);
return true;
}
}
return false;
}
_increment_descriptor_index();
bool next_packet()
{
/* Find next available descriptor (head) holding a packet. */
for (unsigned int i=0; i < _max_index(); i++) {
if (_head_available())
return true;
_advance_head();
}
return false;
}
size_t package_length()
Nic::Packet_descriptor get_packet_descriptor()
{
if (!package_available())
return 0;
if (!_head_available())
return Nic::Packet_descriptor(0, 0);
return Status::Length::get(_current_descriptor().status);
}
size_t get_package(char* const package, const size_t max_length)
{
if (!package_available())
return 0;
const Status::access_t status = _current_descriptor().status;
const Status::access_t status = _head().status;
if (!Status::Start_of_frame::get(status) || !Status::End_of_frame::get(status)) {
warning("Package splitted over more than one descriptor. Package ignored!");
warning("Packet split over more than one descriptor. Packet ignored!");
_set_package_processed();
return 0;
_reset_descriptor(_head_index(), _head().addr);
return Nic::Packet_descriptor(0, 0);
}
const size_t length = Status::Length::get(status);
if (length > max_length) {
warning("Buffer for received package to small. Package ignored!");
/* reset status */
_head().status = 0;
_set_package_processed();
return 0;
}
const char* const src_buffer = _current_buffer();
memcpy(package, src_buffer, length);
_set_package_processed();
return length;
return Nic::Packet_descriptor((addr_t)Addr::Addr31to2::masked(_head().addr) - _phys_base, length);
}
void show_mem_diffs()
{
static unsigned int old_data[0x1F];
log("Rx buffer:");
const unsigned int* const cur_data = local_addr<unsigned int>();
for (unsigned i=0; i<sizeof(old_data)/sizeof(old_data[0]); i++) {
if (cur_data[i] != old_data[i]) {
log(i*4, ": ", Hex(old_data[i]), " -> ", Hex(cur_data[i]));
}
}
memcpy(old_data, cur_data, sizeof(old_data));
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_ */

View File

@ -1,11 +1,12 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
* Copyright (C) 2015-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -25,7 +26,7 @@ using namespace Genode;
class Tx_buffer_descriptor : public Buffer_descriptor
{
private:
enum { BUFFER_COUNT = 2 };
enum { BUFFER_COUNT = 1024 };
struct Addr : Register<0x00, 32> {};
struct Status : Register<0x04, 32> {
@ -33,51 +34,109 @@ class Tx_buffer_descriptor : public Buffer_descriptor
struct Last_buffer : Bitfield<15, 1> {};
struct Wrap : Bitfield<30, 1> {};
struct Used : Bitfield<31, 1> {};
struct Chksum_err : Bitfield<20, 2> {};
};
class Package_send_timeout : public Genode::Exception {};
Timer::Connection &_timer;
public:
Tx_buffer_descriptor(Genode::Env &env, Timer::Connection &timer)
: Buffer_descriptor(env, BUFFER_COUNT), _timer(timer)
{
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
_descriptors[i].status = Status::Used::bits(1) | Status::Last_buffer::bits(1);
}
_descriptors[BUFFER_COUNT-1].status |= Status::Wrap::bits(1);
addr_t const _phys_base;
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
if (i > _max_index())
return;
/* set physical buffer address */
_descriptors[i].addr = phys_addr;
/* set used by SW, also we do not use frame scattering */
_descriptors[i].status = Status::Used::bits(1) |
Status::Last_buffer::bits(1);
/* last buffer must be marked by Wrap bit */
if (i == _max_index())
_descriptors[i].status |= Status::Wrap::bits(1);
}
public:
void add_to_queue(const char* const packet, const size_t size)
class Package_send_timeout : public Genode::Exception {};
Tx_buffer_descriptor(Genode::Env &env,
Nic::Session::Rx::Sink &sink,
Timer::Connection &timer)
: Buffer_descriptor(env, BUFFER_COUNT), _timer(timer),
_phys_base(Dataspace_client(sink.dataspace()).phys_addr())
{
if (size > MAX_PACKAGE_SIZE) {
for (size_t i=0; i <= _max_index(); i++) {
/* configure all descriptors with address 0, which we
* interpret as invalid */
_reset_descriptor(i, 0x0);
}
}
void submit_acks(Nic::Session::Rx::Sink &sink)
{
/* the tail marks the descriptor for which we wait to
* be handed over to software */
for (size_t i=0; i <= _queued(); i++) {
/* stop if still in use by hardware */
if (!Status::Used::get(_tail().status))
break;
/* if descriptor has been configured properly */
if (_tail().addr != 0) {
/* build packet descriptor from buffer descriptor
* and acknowledge packet */
const size_t length = Status::Length::get(_tail().status);
Nic::Packet_descriptor p((addr_t)_tail().addr - _phys_base, length);
if (sink.packet_valid(p))
sink.acknowledge_packet(p);
/* erase address so that we don't send an ack again */
_tail().addr = 0;
/* TODO optionally, we may evaluate the Tx status here */
}
_advance_tail();
}
}
void add_to_queue(Nic::Packet_descriptor p)
{
/* the head marks the descriptor that we use next for
* handing over the packet to hardware */
if (p.size() > BUFFER_SIZE) {
warning("Ethernet package to big. Not sent!");
return;
}
/* wait until the used bit is set (timeout after 200ms) */
uint32_t timeout = 200;
while ( !Status::Used::get(_current_descriptor().status) ) {
if (timeout <= 0) {
throw Package_send_timeout();
}
timeout--;
_timer.msleep(1);
addr_t const packet_phys = _phys_base + p.offset();
if (packet_phys & 0x1f) {
warning("Packet is not aligned properly.");
}
/* wait until the used bit is set (timeout after 10ms) */
uint32_t timeout = 10000;
while ( !Status::Used::get(_head().status) ) {
if (timeout == 0) {
throw Package_send_timeout();
}
timeout -= 1000;
memcpy(_current_buffer(), packet, size);
/* TODO buffer is full, instead of sleeping we should
* therefore wait for tx_complete interrupt */
_timer.usleep(1000);
}
_current_descriptor().status &= Status::Length::clear_mask();
_current_descriptor().status |= Status::Length::bits(size);
_reset_descriptor(_head_index(), packet_phys);
_head().status |= Status::Length::bits(p.size());
/* unset the unset bit */
_current_descriptor().status &= Status::Used::clear_mask();
/* unset the used bit */
_head().status &= Status::Used::clear_mask();
_increment_descriptor_index();
_advance_head();
}
};