mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 10:01:57 +00:00
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:
parent
8ad56a6c0e
commit
dc0bfd7008
@ -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_ */
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user