Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ri/tech-hub/apps/brainscales/libnux
1 result
Show changes
Commits on Source (8)
Showing
with 310 additions and 251 deletions
...@@ -133,20 +133,12 @@ bool Queue<N, T>::pop(T& t) ...@@ -133,20 +133,12 @@ bool Queue<N, T>::pop(T& t)
template <size_t N, class T> template <size_t N, class T>
T& Queue<N, T>::operator[](size_t index) T& Queue<N, T>::operator[](size_t index)
{ {
// range check and if overrange return last element
if (!(index < m_size_current)) {
index = m_size_current - 1;
}
return m_array[((N + m_write_addr) - m_size_current + index) % N]; return m_array[((N + m_write_addr) - m_size_current + index) % N];
} }
template <size_t N, class T> template <size_t N, class T>
T const& Queue<N, T>::operator[](size_t index) const T const& Queue<N, T>::operator[](size_t index) const
{ {
// range check and if overrange return last element
if (!(index < m_size_current)) {
index = m_size_current - 1;
}
return m_array[((N + m_write_addr) - m_size_current + index) % N]; return m_array[((N + m_write_addr) - m_size_current + index) % N];
} }
......
...@@ -25,15 +25,12 @@ public: ...@@ -25,15 +25,12 @@ public:
/** /**
* Scheduler loop. * Scheduler loop.
* \tparam Signaller Scheduler loop control type * \tparam Signaller Scheduler loop control type
* \tparam S Service types
* \tparam E Event source types * \tparam E Event source types
* \param signaller Scheduler loop control instance * \param signaller Scheduler loop control instance
* \param services Tuple of references to services
* \param event_sources Tuple of references to event sources * \param event_sources Tuple of references to event sources
*/ */
template <typename Signaller, typename... S, typename... E> template <typename Signaller, typename... E>
void execute( void execute(Signaller& signaller, std::tuple<E&...>& event_sources);
Signaller& signaller, std::tuple<S&...>& services, std::tuple<E&...>& event_sources);
/** /**
* Get maximal number of elements stored in queue since creation. * Get maximal number of elements stored in queue since creation.
...@@ -67,11 +64,8 @@ public: ...@@ -67,11 +64,8 @@ public:
/** /**
* Execute services task corresponding to earliest deadline event in queue. * Execute services task corresponding to earliest deadline event in queue.
* \tparam S Service types
* \param services Tuple of references to services to execute from
*/ */
template <typename... S> void run_queue_service();
void run_queue_service(std::tuple<S&...>& services);
private: private:
/** /**
...@@ -81,10 +75,11 @@ private: ...@@ -81,10 +75,11 @@ private:
* \tparam Is Succeeding indices * \tparam Is Succeeding indices
* \param sources Tuple of references to event sources * \param sources Tuple of references to event sources
* \param t Time to fetch events for * \param t Time to fetch events for
* \param e Event object storage to use for fetching from timers
*/ */
template <typename... E, size_t I, size_t... Is> template <typename... E, size_t I, size_t... Is>
void fetch_events_from_source( void fetch_events_from_source(
std::tuple<E...>& sources, std::index_sequence<I, Is...>, time_type t); std::tuple<E...>& sources, std::index_sequence<I, Is...>, time_type t, Event& e);
/** /**
* End of fetch events iterating over recursively all event sources. * End of fetch events iterating over recursively all event sources.
...@@ -92,31 +87,11 @@ private: ...@@ -92,31 +87,11 @@ private:
* \tparam I Index * \tparam I Index
* \param sources Tuple of references to event sources * \param sources Tuple of references to event sources
* \param t Time to fetch events for * \param t Time to fetch events for
* \param e Event object storage to use for fetching from timers
*/ */
template <typename... E, size_t I> template <typename... E, size_t I>
void fetch_events_from_source(std::tuple<E...>& sources, std::index_sequence<I>, time_type t); void fetch_events_from_source(
std::tuple<E...>& sources, std::index_sequence<I>, time_type t, Event& e);
/**
* Execute by interating recursively over all services.
* \tparam S Services types
* \tparam I First index
* \tparam Is Succeeding indices
* \param sources Tuple of references to services
* \param id Service ID of service to execute
*/
template <typename... S, size_t I, size_t... Is>
void run_service(std::tuple<S...>& services, std::index_sequence<I, Is...>, service_id id);
/**
* End of execute by interating recursively over all services.
* \tparam S Services types
* \tparam I Index
* \param sources Tuple of references to services
* \param id Service ID of service to execute
*/
template <typename... S, size_t I>
void run_service(std::tuple<S...>& services, std::index_sequence<I>, service_id id);
}; };
template <size_t queue_size> template <size_t queue_size>
...@@ -146,27 +121,26 @@ template <typename... E> ...@@ -146,27 +121,26 @@ template <typename... E>
inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_timed( inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_timed(
std::tuple<E&...>& event_sources, time_type t) std::tuple<E&...>& event_sources, time_type t)
{ {
fetch_events_from_source(event_sources, std::make_index_sequence<sizeof...(E)>(), t); Event e;
fetch_events_from_source(event_sources, std::make_index_sequence<sizeof...(E)>(), t, e);
} }
template <size_t queue_size> template <size_t queue_size>
template <typename... E, size_t I, size_t... Is> template <typename... E, size_t I, size_t... Is>
inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_from_source( inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_from_source(
std::tuple<E...>& sources, std::index_sequence<I, Is...>, time_type t) std::tuple<E...>& sources, std::index_sequence<I, Is...>, time_type t, Event& e)
{ {
Event e;
while (std::get<sizeof...(E) - 1 - sizeof...(Is)>(sources).next_event(e, t)) { while (std::get<sizeof...(E) - 1 - sizeof...(Is)>(sources).next_event(e, t)) {
m_queue.push(e); m_queue.push(e);
} }
fetch_events_from_source(sources, std::make_index_sequence<sizeof...(Is)>(), t); fetch_events_from_source(sources, std::make_index_sequence<sizeof...(Is)>(), t, e);
} }
template <size_t queue_size> template <size_t queue_size>
template <typename... E, size_t I> template <typename... E, size_t I>
inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_from_source( inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_from_source(
std::tuple<E...>& sources, std::index_sequence<I>, time_type t) std::tuple<E...>& sources, std::index_sequence<I>, time_type t, Event& e)
{ {
Event e;
while (std::get<sizeof...(E) - 1>(sources).next_event(e, t)) { while (std::get<sizeof...(E) - 1>(sources).next_event(e, t)) {
m_queue.push(e); m_queue.push(e);
} }
...@@ -175,58 +149,37 @@ inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_f ...@@ -175,58 +149,37 @@ inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_f
template <size_t queue_size> template <size_t queue_size>
inline __attribute__((always_inline)) void Scheduler<queue_size>::sort_earliest_first() inline __attribute__((always_inline)) void Scheduler<queue_size>::sort_earliest_first()
{ {
// linear search for earliest deadline size_t const current_queue_size = m_queue.get_size();
time_type earliest = m_queue[0].deadline; if (current_queue_size > 1) {
size_t min_index = 0; // linear search for earliest deadline
for (size_t i = 1; i < m_queue.get_size(); ++i) { time_type earliest = m_queue[0].deadline;
if (m_queue[i].deadline < earliest) { size_t min_index = 0;
min_index = i; for (size_t i = 1; i < current_queue_size; ++i) {
earliest = m_queue[i].deadline; time_type const local_deadline = m_queue[i].deadline;
if (local_deadline < earliest) {
min_index = i;
earliest = local_deadline;
}
}
// set earliest deadline event to start of queue
if (min_index != 0) {
std::swap(m_queue[0], m_queue[min_index]);
} }
} }
// set earliest deadline event to start of queue
Event tmp = m_queue[0];
m_queue[0] = m_queue[min_index];
m_queue[min_index] = tmp;
} }
template <size_t queue_size> template <size_t queue_size>
template <typename... S> inline __attribute__((always_inline)) void Scheduler<queue_size>::run_queue_service()
inline __attribute__((always_inline)) void Scheduler<queue_size>::run_queue_service(
std::tuple<S&...>& services)
{ {
Event e; Event e;
if (m_queue.pop(e)) { if (m_queue.pop(e)) {
run_service(services, std::make_index_sequence<sizeof...(S)>(), e.id); (*e.service)();
}
}
template <size_t queue_size>
template <typename... S, size_t I, size_t... Is>
inline __attribute__((always_inline)) void Scheduler<queue_size>::run_service(
std::tuple<S...>& services, std::index_sequence<I, Is...>, service_id id)
{
if (std::get<sizeof...(Is)>(services).id == id) {
std::get<sizeof...(Is)>(services).exec();
} else {
run_service(services, std::make_index_sequence<sizeof...(Is)>(), id);
}
}
template <size_t queue_size>
template <typename... S, size_t I>
inline __attribute__((always_inline)) void Scheduler<queue_size>::run_service(
std::tuple<S...>& services, std::index_sequence<I>, service_id id)
{
if (std::get<0>(services).id == id) {
std::get<0>(services).exec();
} }
} }
template <size_t queue_size> template <size_t queue_size>
template <typename Signaller, typename... S, typename... E> template <typename Signaller, typename... E>
void Scheduler<queue_size>::execute( void Scheduler<queue_size>::execute(Signaller& signaller, std::tuple<E&...>& event_sources)
Signaller& signaller, std::tuple<S&...>& services, std::tuple<E&...>& event_sources)
{ {
scheduler_signal signal = signaller.signal(); scheduler_signal signal = signaller.signal();
while (not(signal == scheduler_exit)) { while (not(signal == scheduler_exit)) {
...@@ -236,7 +189,7 @@ void Scheduler<queue_size>::execute( ...@@ -236,7 +189,7 @@ void Scheduler<queue_size>::execute(
fetch_events_timed(event_sources, get_time()); fetch_events_timed(event_sources, get_time());
} }
sort_earliest_first(); sort_earliest_first();
run_queue_service(services); run_queue_service();
} }
} }
signal = signaller.signal(); signal = signaller.signal();
......
#pragma once #pragma once
#include "libnux/scheduling/types.hpp" #include <functional>
// Service is a template that can execute // Service is a function pointer to be executed
// an arbitrary function or class member function typedef std::function<void(void)> Service;
// by calling service.exec().
// A Service is identified by ID.
// Service for functions.
template <service_id ID, void (*Function)()>
class Service_Function
{
public:
static constexpr service_id id = ID;
Service_Function() {}
static constexpr auto exec = Function;
};
// Service for class member functions.
template <service_id ID, typename C, void (C::*function)()>
class Service_Class
{
C& m_c;
public:
static constexpr service_id id = ID;
Service_Class() {}
Service_Class(C& c) : m_c(c) {}
inline __attribute__((always_inline)) auto exec() -> decltype((this->m_c.*function)())
{
return (this->m_c.*function)();
}
};
...@@ -11,22 +11,20 @@ class Timer ...@@ -11,22 +11,20 @@ class Timer
time_type m_period; time_type m_period;
time_type m_start; time_type m_start;
// Event to be run // Event to be run
service_id m_id; Service m_service;
// debug // debug
// count missed runs // count missed runs
time_type m_missed; time_type m_missed;
time_type m_last_run; time_type m_last_run;
time_type m_end_run;
public: public:
Timer(); Timer(Service service);
// get next event, return true, if new event // get next event, return true, if new event
bool next_event(Event& event, time_type t); bool next_event(Event& event, time_type t);
// set service id directly // set service id directly
void set_service_id(service_id id); void set_service(Service service);
// set service id by service
template <class S>
void set_service(S& s);
// set timing // set timing
void set_first_deadline(time_type first); void set_first_deadline(time_type first);
time_type get_first_deadline() const; time_type get_first_deadline() const;
...@@ -37,30 +35,27 @@ public: ...@@ -37,30 +35,27 @@ public:
time_type get_missed_count(); time_type get_missed_count();
}; };
inline Timer::Timer() inline Timer::Timer(Service service)
{ {
this->m_id = 0;
this->m_start = 0; this->m_start = 0;
this->m_num_periods = 0; this->m_num_periods = 0;
this->m_period = 0; this->m_period = 0;
this->m_last_run = 0; this->m_last_run = 0;
this->m_end_run = 0;
this->m_missed = 0; this->m_missed = 0;
this->m_service = service;
} }
inline void Timer::set_service_id(service_id id) inline void Timer::set_service(Service service)
{ {
this->m_id = id; this->m_service = service;
}
template <class T>
void Timer::set_service(T& t)
{
this->m_id = t.id;
} }
inline void Timer::set_first_deadline(time_type first) inline void Timer::set_first_deadline(time_type first)
{ {
this->m_start = first; this->m_start = first;
m_last_run = first;
m_end_run = m_start + m_period * m_num_periods;
} }
inline time_type Timer::get_first_deadline() const inline time_type Timer::get_first_deadline() const
...@@ -71,28 +66,26 @@ inline time_type Timer::get_first_deadline() const ...@@ -71,28 +66,26 @@ inline time_type Timer::get_first_deadline() const
inline void Timer::set_num_periods(time_type num) inline void Timer::set_num_periods(time_type num)
{ {
this->m_num_periods = num; this->m_num_periods = num;
m_end_run = m_start + m_period * m_num_periods;
} }
inline void Timer::set_period(time_type period) inline void Timer::set_period(time_type period)
{ {
this->m_period = period; this->m_period = period;
m_end_run = m_start + m_period * m_num_periods;
} }
inline bool Timer::next_event(Event& event, time_type t) inline bool Timer::next_event(Event& event, time_type t)
{ {
if (t >= this->m_start) { if ((t >= m_last_run) && (t < m_end_run)) {
if ((t -= this->m_start) < this->m_period * this->m_num_periods) { this->m_last_run += this->m_period;
if (t >= this->m_last_run) { while (this->m_last_run < t) {
this->m_last_run += this->m_period; ++this->m_missed;
while (this->m_last_run < t) { this->m_last_run += this->m_period;
++this->m_missed;
this->m_last_run += this->m_period;
}
event.id = this->m_id;
event.deadline = this->m_last_run + this->m_start;
return true;
}
} }
event.service = &(this->m_service);
event.deadline = this->m_last_run;
return true;
} }
return false; return false;
} }
......
#pragma once #pragma once
#include "libnux/scheduling/Service.hpp"
#include "libnux/scheduling/types.hpp" #include "libnux/scheduling/types.hpp"
class TimerOneshot class TimerOneshot
...@@ -9,45 +10,45 @@ class TimerOneshot ...@@ -9,45 +10,45 @@ class TimerOneshot
// earliest start time = m_deadline - m_window // earliest start time = m_deadline - m_window
time_type m_window; time_type m_window;
// Event to be run // Event to be run
service_id m_id; Service m_service;
bool m_has_been_run; bool m_has_been_run;
public: public:
TimerOneshot(); TimerOneshot(Service service);
TimerOneshot(time_type t); TimerOneshot(Service service, time_type t);
TimerOneshot(time_type deadline, time_type window); TimerOneshot(Service service, time_type deadline, time_type window);
// get 'next' event, return true, if new event // get 'next' event, return true, if new event
bool next_event(Event& event, time_type t); bool next_event(Event& event, time_type t);
// set service id directly // set service
void set_service_id(service_id id); void set_service(Service service);
// set service id by service
template <class S>
void set_service(S& s);
// set timing // set timing
void set_deadline(time_type t); void set_deadline(time_type t);
void set_window(time_type window); void set_window(time_type window);
}; };
TimerOneshot::TimerOneshot() TimerOneshot::TimerOneshot(Service service)
{ {
this->m_deadline = 0; this->m_deadline = 0;
this->m_window = 0; this->m_window = 0;
this->m_has_been_run = true; this->m_has_been_run = true;
this->m_service = service;
} }
TimerOneshot::TimerOneshot(time_type deadline) TimerOneshot::TimerOneshot(Service service, time_type deadline)
{ {
this->m_deadline = deadline; this->m_deadline = deadline;
this->m_window = 0; this->m_window = 0;
this->m_has_been_run = false; this->m_has_been_run = false;
this->m_service = service;
} }
TimerOneshot::TimerOneshot(time_type deadline, time_type window) TimerOneshot::TimerOneshot(Service service, time_type deadline, time_type window)
{ {
this->m_deadline = deadline; this->m_deadline = deadline;
this->m_window = window; this->m_window = window;
this->m_has_been_run = false; this->m_has_been_run = false;
this->m_service = service;
} }
void TimerOneshot::set_deadline(time_type t) void TimerOneshot::set_deadline(time_type t)
...@@ -62,21 +63,15 @@ void TimerOneshot::set_window(time_type t) ...@@ -62,21 +63,15 @@ void TimerOneshot::set_window(time_type t)
this->m_has_been_run = false; this->m_has_been_run = false;
} }
void TimerOneshot::set_service_id(service_id id) void TimerOneshot::set_service(Service service)
{ {
this->m_id = id; this->m_service = service;
}
template <class S>
void TimerOneshot::set_service(S& s)
{
this->m_id = s.id;
} }
bool TimerOneshot::next_event(Event& event, time_type t) bool TimerOneshot::next_event(Event& event, time_type t)
{ {
if (not this->m_has_been_run and t >= this->m_deadline - this->m_window) { if (not this->m_has_been_run and t >= this->m_deadline - this->m_window) {
event.id = this->m_id; event.service = &(this->m_service);
event.deadline = this->m_deadline; event.deadline = this->m_deadline;
this->m_has_been_run = true; this->m_has_been_run = true;
return true; return true;
......
#pragma once #pragma once
#include "libnux/scheduling/Service.hpp"
#include "libnux/vx/spr.h" #include "libnux/vx/spr.h"
#include <stdint.h> #include <stdint.h>
typedef uint8_t service_id;
#ifdef LIBNUX_TIME64 #ifdef LIBNUX_TIME64
typedef uint64_t time_type; typedef uint64_t time_type;
#else #else
...@@ -17,7 +16,7 @@ inline time_type get_time() ...@@ -17,7 +16,7 @@ inline time_type get_time()
struct Event struct Event
{ {
service_id id = 0; Service const* service;
time_type deadline; time_type deadline;
}; };
......
...@@ -30,6 +30,7 @@ static uint32_t const dls_config_odd_base = 0x00020000; ...@@ -30,6 +30,7 @@ static uint32_t const dls_config_odd_base = 0x00020000;
static uint32_t const dls_raw_base = 0x000f0000; static uint32_t const dls_raw_base = 0x000f0000;
static uint32_t const dls_randgen_base = 0x000e0000; static uint32_t const dls_randgen_base = 0x000e0000;
static uint32_t const dls_extmem_base = 0x80000000; static uint32_t const dls_extmem_base = 0x80000000;
static uint32_t const dls_extmem_dram_base = 0x84000000;
static uint32_t const dls_neuron_reset_base = 0x00c90000 + dls_num_rows * dls_num_vectors_per_row; static uint32_t const dls_neuron_reset_base = 0x00c90000 + dls_num_rows * dls_num_vectors_per_row;
// clang-format on // clang-format on
...@@ -81,6 +82,7 @@ static constexpr omnibus_address_t cadc_top_acausal_base_address = ...@@ -81,6 +82,7 @@ static constexpr omnibus_address_t cadc_top_acausal_base_address =
/* External memory base address for scalar data access */ /* External memory base address for scalar data access */
static constexpr uint32_t extmem_data_base = 1ul << 30; static constexpr uint32_t extmem_data_base = 1ul << 30;
static constexpr uint32_t extmem_dram_data_base = 1ul << 30 | 1ul << 28;
static constexpr uint32_t vecgen_top_base_address = extmem_data_base | (1ul << 28); static constexpr uint32_t vecgen_top_base_address = extmem_data_base | (1ul << 28);
static constexpr uint32_t vecgen_bottom_base_address = extmem_data_base | (1ul << 28) | 0x4000; static constexpr uint32_t vecgen_bottom_base_address = extmem_data_base | (1ul << 28) | 0x4000;
......
...@@ -158,20 +158,21 @@ public: ...@@ -158,20 +158,21 @@ public:
/** ExtMem address type representing a "external memory" address. */ /** ExtMem address type representing a "external memory" address. */
class ExtMem template <uint32_t ScalarBaseAddress, uint32_t VectorBaseAddress, size_t WordSize>
class ExtMemBase
{ {
public: public:
/** Convert to "vector unit" address. */ /** Convert to "vector unit" address. */
constexpr byte_address to_vector_addr() const constexpr byte_address to_vector_addr() const
{ {
return (m_ptr >> 2) | dls_extmem_base; return (m_ptr >> 2) | VectorBaseAddress;
} }
/** Convert to "vector fxvoutx" address. */ /** Convert to "vector fxvoutx" address. */
constexpr byte_address to_fxviox_addr() const constexpr byte_address to_fxviox_addr() const
{ {
// Whatever... // Whatever...
return (m_ptr >> 4) | dls_extmem_base; return (m_ptr >> 4) | VectorBaseAddress;
} }
/** Convert to "vector unit" pointer. */ /** Convert to "vector unit" pointer. */
...@@ -193,7 +194,7 @@ public: ...@@ -193,7 +194,7 @@ public:
/** Convert to "scalar unit" address. */ /** Convert to "scalar unit" address. */
constexpr byte_address to_scalar_addr() const constexpr byte_address to_scalar_addr() const
{ {
return m_ptr | extmem_data_base; return m_ptr | ScalarBaseAddress;
} }
/** Convert to "scalar unit" pointer. */ /** Convert to "scalar unit" pointer. */
...@@ -208,11 +209,13 @@ public: ...@@ -208,11 +209,13 @@ public:
private: private:
friend GlobalAddress; friend GlobalAddress;
inline constexpr ExtMem(omnibus_address address, byte_address offset); inline constexpr ExtMemBase(omnibus_address address, byte_address offset);
byte_address m_ptr; byte_address m_ptr;
}; };
typedef ExtMemBase<extmem_data_base, dls_extmem_base, 1ull << 15> ExtMem;
typedef ExtMemBase<extmem_dram_data_base, dls_extmem_dram_base, 1ull << 26> ExtMemDRAM;
/** SRAM address type representing a "SRAM memory" address on each PPU. */ /** SRAM address type representing a "SRAM memory" address on each PPU. */
class SRAM class SRAM
...@@ -317,6 +320,16 @@ public: ...@@ -317,6 +320,16 @@ public:
return to_extmem(); return to_extmem();
} }
/** Convert a GlobalAddress to an ExtMemDRAM address. */
constexpr ExtMemDRAM to_extmem_dram() const
{
return ExtMemDRAM(m_addr, m_offset);
}
constexpr explicit operator ExtMemDRAM() const
{
return to_extmem_dram();
}
/** Convert a GlobalAddress to an SRAM address. */ /** Convert a GlobalAddress to an SRAM address. */
inline constexpr SRAM to_sram() const; inline constexpr SRAM to_sram() const;
constexpr explicit operator SRAM() const constexpr explicit operator SRAM() const
...@@ -334,6 +347,9 @@ public: ...@@ -334,6 +347,9 @@ public:
/** Check if global address represents a valid address in extmem. */ /** Check if global address represents a valid address in extmem. */
inline constexpr bool is_extmem() const; inline constexpr bool is_extmem() const;
/** Check if global address represents a valid address in extmem dram. */
inline constexpr bool is_extmem_dram() const;
/** Check if global address represents a valid address in SRAMs. */ /** Check if global address represents a valid address in SRAMs. */
inline constexpr bool is_sram() const; inline constexpr bool is_sram() const;
...@@ -396,22 +412,31 @@ struct AddressSpace ...@@ -396,22 +412,31 @@ struct AddressSpace
static_assert(vecgen_top.to_global_omnibus() == 0x8400'0000); static_assert(vecgen_top.to_global_omnibus() == 0x8400'0000);
static_assert(vecgen_bot.to_global_omnibus() == 0x8400'1000); static_assert(vecgen_bot.to_global_omnibus() == 0x8400'1000);
/** External memory (FPGA-connected DRAM). */ /** External memory (FPGA-connected SRAM). */
GlobalAddress static constexpr extmem = GlobalAddress static constexpr extmem =
GlobalAddress::from_global(1ull << 31 | 0 << 27 | 0 << 26); GlobalAddress::from_global(1ull << 31 | 0 << 27 | 0 << 26);
constexpr static uint32_t extmem_size{1 << 29}; // 128MWords (or 512MiB) of extmem constexpr static uint32_t extmem_size{1 << 15}; // 32KWords (or 128KiB) of extmem
static_assert(extmem.to_global_omnibus() == 0x8000'0000); static_assert(extmem.to_global_omnibus() == 0x8000'0000);
/** External memory (FPGA-connected DRAM). */
GlobalAddress static constexpr extmem_dram =
GlobalAddress::from_global(1ull << 31 | 1ull << 26 | 0 << 26);
constexpr static uint32_t extmem_dram_size{1ull << 26}; // 64MWords (or 256MiB) of extmem
static_assert(extmem_dram.to_global_omnibus() == 0x8400'0000);
}; };
constexpr GlobalAddress::ExtMem::ExtMem( template <uint32_t ScalarBaseAddress, uint32_t VectorBaseAddress, size_t WordSize>
constexpr GlobalAddress::ExtMemBase<ScalarBaseAddress, VectorBaseAddress, WordSize>::ExtMemBase(
GlobalAddress::omnibus_address const address, GlobalAddress::byte_address const offset) : GlobalAddress::omnibus_address const address, GlobalAddress::byte_address const offset) :
m_ptr((address << 2) | offset) m_ptr((address << 2) | offset)
{} {}
constexpr GlobalAddress::ExtMem::operator bool() const template <uint32_t ScalarBaseAddress, uint32_t VectorBaseAddress, size_t WordSize>
constexpr GlobalAddress::ExtMemBase<ScalarBaseAddress, VectorBaseAddress, WordSize>::operator bool()
const
{ {
return m_ptr < static_cast<byte_address>(AddressSpace::extmem_size << 2); return m_ptr < static_cast<byte_address>(WordSize << 2);
} }
constexpr GlobalAddress::SRAM::operator bool() const constexpr GlobalAddress::SRAM::operator bool() const
...@@ -511,6 +536,15 @@ constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::ExtMem>( ...@@ -511,6 +536,15 @@ constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::ExtMem>(
return GlobalAddress(address, byte_offset); return GlobalAddress(address, byte_offset);
} }
template <>
constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::ExtMemDRAM>(
GlobalAddress::byte_address offset)
{
omnibus_address const address = (offset >> 2) | AddressSpace::extmem_dram.to_global_omnibus();
byte_address const byte_offset = offset % 4;
return GlobalAddress(address, byte_offset);
}
template <> template <>
constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::SRAM>( constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::SRAM>(
GlobalAddress::byte_address offset, PPUOnDLS me) GlobalAddress::byte_address offset, PPUOnDLS me)
...@@ -541,6 +575,12 @@ constexpr bool GlobalAddress::is_extmem() const ...@@ -541,6 +575,12 @@ constexpr bool GlobalAddress::is_extmem() const
(m_addr < (AddressSpace::extmem.to_global_omnibus() + AddressSpace::extmem_size)); (m_addr < (AddressSpace::extmem.to_global_omnibus() + AddressSpace::extmem_size));
} }
constexpr bool GlobalAddress::is_extmem_dram() const
{
return (m_addr >= AddressSpace::extmem_dram.to_global_omnibus()) &&
(m_addr < (AddressSpace::extmem_dram.to_global_omnibus() + AddressSpace::extmem_size));
}
constexpr bool GlobalAddress::is_sram() const constexpr bool GlobalAddress::is_sram() const
{ {
return (m_addr >= AddressSpace::sram_top.to_global_omnibus()) && return (m_addr >= AddressSpace::sram_top.to_global_omnibus()) &&
......
...@@ -20,7 +20,7 @@ void sleep_cycles(uint32_t cycles); ...@@ -20,7 +20,7 @@ void sleep_cycles(uint32_t cycles);
because otherwise, mfsprs might get grouped together, because otherwise, mfsprs might get grouped together,
rendering measurement userless. rendering measurement userless.
*/ */
time_base_t now(); time_base_t now() ATTRIB_LINK_TO_INTERNAL;
/* /*
Pair of time values. Pair of time values.
......
...@@ -2,6 +2,8 @@ MEMORY { ...@@ -2,6 +2,8 @@ MEMORY {
int_mem(rwx) : ORIGIN = 0x00000000, LENGTH = 16K int_mem(rwx) : ORIGIN = 0x00000000, LENGTH = 16K
ext_mem(rwx) : ORIGIN = 0x80000000, LENGTH = 128K ext_mem(rwx) : ORIGIN = 0x80000000, LENGTH = 128K
ext_mem_data(rw) : ORIGIN = 0x40000000, LENGTH = 128K /* Same memory as ext_mem */ ext_mem_data(rw) : ORIGIN = 0x40000000, LENGTH = 128K /* Same memory as ext_mem */
ext_mem_dram(rwx) : ORIGIN = 0x90000000, LENGTH = 256M
ext_mem_dram_data(rw) : ORIGIN = 0x50000000, LENGTH = 256M /* Same memory as ext_mem_dram */
} }
/* ECM(2017-12-04): We should provide this numbers from waf configure step FIXME */ /* ECM(2017-12-04): We should provide this numbers from waf configure step FIXME */
...@@ -119,6 +121,29 @@ SECTIONS { ...@@ -119,6 +121,29 @@ SECTIONS {
/* global symbol marking the end of ext_mem_data data sections */ /* global symbol marking the end of ext_mem_data data sections */
_ext_end_data = .; _ext_end_data = .;
. = ORIGIN(ext_mem_dram);
ext_dram.text : {
/* explicitly placed user code */
*(ext_dram.text*);
} > ext_mem_dram
/* global symbol marking the end of ext_mem text sections */
_ext_dram_end = .;
/* all extmem data is placed after text sections */
ext_dram_data_offset = . - ORIGIN(ext_mem_dram);
. = ORIGIN(ext_mem_dram_data) + ext_dram_data_offset;
_ext_dram_data_begin = .;
ext_dram.data (ORIGIN(ext_mem_dram_data) + ext_dram_data_offset) :
{
*(ext_dram.data*);
KEEP(*(ext_dram.data.keep*));
} > ext_mem_dram_data
/* global symbol marking the end of ext_mem_dram_data data sections */
_ext_dram_end_data = .;
} }
. = _int_end; . = _int_end;
. = ALIGN(16); . = ALIGN(16);
......
#include <cstdlib>
#if !__has_builtin(__builtin_abort)
#define __builtin_abort() do { \
exit(1); \
} while (0)
#endif
namespace std {
void __throw_out_of_range_fmt(const char*, ...) {
__builtin_abort();
}
} // std
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
using namespace libnux::vx; using namespace libnux::vx;
uint32_t data_ext __attribute__((section("ext.data"))); uint32_t data_ext __attribute__((section("ext.data")));
uint32_t data_ext_dram __attribute__((section("ext_dram.data")));
uint32_t data_int; uint32_t data_int;
void start() void start()
...@@ -12,6 +13,8 @@ void start() ...@@ -12,6 +13,8 @@ void start()
testcase_begin("data placement"); testcase_begin("data placement");
test_true(reinterpret_cast<uint32_t>(&data_ext) & extmem_data_base); test_true(reinterpret_cast<uint32_t>(&data_ext) & extmem_data_base);
test_true(!(reinterpret_cast<uint32_t>(&data_int) & extmem_data_base)); test_true(!(reinterpret_cast<uint32_t>(&data_int) & extmem_data_base));
test_true(reinterpret_cast<uint32_t>(&data_ext_dram) & extmem_dram_data_base);
test_true(!(reinterpret_cast<uint32_t>(&data_int) & extmem_dram_data_base));
testcase_end(); testcase_end();
test_summary(); test_summary();
test_shutdown(); test_shutdown();
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
using namespace libnux::vx; using namespace libnux::vx;
template <typename T> template <typename T, size_t BaseAddress = extmem_data_base>
void test_write_read(T const value, size_t const offset = (1 << 16) + 13) void test_write_read(T const value, size_t const offset = (1 << 16) + 13)
{ {
T* ptr = (T*) (extmem_data_base + offset * sizeof(T)); T* ptr = (T*) (BaseAddress + offset * sizeof(T));
*ptr = value; *ptr = value;
// cf. issue #3739 // cf. issue #3739
asm volatile("nop"); asm volatile("nop");
...@@ -27,6 +27,14 @@ void start(void) ...@@ -27,6 +27,14 @@ void start(void)
test_write_read<uint8_t>(17 + 4, 0x20000 - 1); // w/r highest extmem address test_write_read<uint8_t>(17 + 4, 0x20000 - 1); // w/r highest extmem address
testcase_end(); testcase_end();
testcase_begin("external dram memory write read via scalar unit");
test_write_read<uint8_t, extmem_dram_data_base>(123);
test_write_read<uint16_t, extmem_dram_data_base>(12345);
test_write_read<uint32_t, extmem_dram_data_base>(12345678);
test_write_read<uint8_t, extmem_dram_data_base>(
17 + 4, 0x10000000 - 1); // w/r highest extmem dram address
testcase_end();
test_summary(); test_summary();
test_shutdown(); test_shutdown();
} }
...@@ -10,22 +10,43 @@ using namespace libnux::vx; ...@@ -10,22 +10,43 @@ using namespace libnux::vx;
void start(void) { void start(void) {
test_init(); test_init();
testcase_begin("external memory write read via vector unit"); {
vector_type values; testcase_begin("external memory write read via vector unit");
for (size_t entry = 0; entry < dls_vector_size; ++entry) { vector_type values;
values[entry] = entry; for (size_t entry = 0; entry < dls_vector_size; ++entry) {
values[entry] = entry;
}
uint32_t const index = 0;
set_vector(values, dls_extmem_base + (1 << 16), index);
auto const read = get_vector(dls_extmem_base, index);
for (size_t column = 0; column < 128; ++column) {
test_equal(read[column], values[column]);
}
testcase_end();
} }
uint32_t const index = 0; {
testcase_begin("external dram memory write read via vector unit");
vector_type values;
for (size_t entry = 0; entry < dls_vector_size; ++entry) {
values[entry] = entry;
}
uint32_t const index = 0;
set_vector(values, dls_extmem_base + (1 << 16), index); set_vector(values, dls_extmem_dram_base + (1 << 16), index);
auto const read = get_vector(dls_extmem_base, index); auto const read = get_vector(dls_extmem_dram_base, index);
for (size_t column = 0; column < 128; ++column) { for (size_t column = 0; column < 128; ++column) {
test_equal(read[column], values[column]); test_equal(read[column], values[column]);
}
testcase_end();
} }
testcase_end();
test_summary(); test_summary();
test_shutdown(); test_shutdown();
......
...@@ -102,6 +102,90 @@ void start() ...@@ -102,6 +102,90 @@ void start()
test_write_string("\n"); test_write_string("\n");
} }
{
testcase_begin("Vector (generated) write to extmem_dram (vector address via vector type), "
"scalar read (generated) of one element");
auto const ga = GlobalAddress::from_global(
AddressSpace::extmem_dram.to_global_omnibus() + next_offset + (1 << 14));
next_offset += 0x100;
*ga.to_extmem_dram().to_scalar<__vector uint16_t>() = vec_splat_u16(0x1);
auto val = *ga.to_extmem_dram().to_scalar<uint16_t>();
test_equal(val, 0x1);
testcase_end();
test_write_string("at address: ");
test_write_string(to_hexstring(ga.to_global_omnibus()).data());
test_write_string("\n");
}
{
testcase_begin("Vector (fxvstax) write to extmem_dram (scalar address), scalar read "
"(generated) of one element");
auto const ga = GlobalAddress::from_global(
AddressSpace::extmem_dram.to_global_omnibus() + next_offset + (1 << 14));
next_offset += 0x100;
__vector uint8_t data = vec_splat_u8(0x2);
// clang-format off
asm volatile(
"fxvstax %[data], %[base], %[index]\n"
"sync\n"
:: [data] "qv" (data), [base] "b" (ga.to_extmem_dram().to_scalar_addr()), [index] "r" (0) : "memory"
);
// clang-format on
auto const val = *ga.to_extmem_dram().to_scalar<uint8_t>();
test_equal(val, 0x2);
testcase_end();
test_write_string("at address: ");
test_write_string(to_hexstring(ga.to_global_omnibus()).data());
test_write_string("\n");
}
{
testcase_begin("Vector (fxvoutx) write to extmem_dram (hioff(scalar) >> 4 | 1<<31), scalar "
"read (generated) of one element");
auto const ga = GlobalAddress::from_global(
AddressSpace::extmem_dram.to_global_omnibus() + next_offset + (1 << 14));
next_offset += 0x100;
__vector uint8_t data = vec_splat_u8(0x9);
uint32_t const address =
((ga.to_extmem_dram().to_scalar_addr() & 0x3fff'ffff) >> 4) | (1ull << 31);
// clang-format off
asm volatile(
"fxvoutx %[data], %[base], %[index]\n"
"sync\n"
:: [data] "qv" (data), [base] "b" (address), [index] "r" (0) : "memory"
);
// clang-format on
auto const val = *ga.to_extmem_dram().to_scalar<uint8_t>();
test_equal(val, 0x9);
testcase_end();
test_write_string("at address: ");
test_write_string(to_hexstring(ga.to_global_omnibus()).data());
test_write_string("\n");
}
{
testcase_begin("Vector (fxvoutx) write to extmem_dram (fxviox address), scalar read "
"(generated) of one element");
auto const ga = GlobalAddress::from_global(
AddressSpace::extmem_dram.to_global_omnibus() + next_offset + (1 << 14));
next_offset += 0x100;
__vector uint8_t data = vec_splat_u8(0xb);
// clang-format off
asm volatile(
"fxvoutx %[data], %[base], %[index]\n"
"sync\n"
:: [data] "qv" (data), [base] "b" (ga.to_extmem_dram().to_fxviox_addr()), [index] "r" (0) : "memory"
);
// clang-format on
auto const val = *ga.to_extmem_dram().to_scalar<uint8_t>();
test_equal(val, 0xb);
testcase_end();
test_write_string("at address: ");
test_write_string(to_hexstring(ga.to_global_omnibus()).data());
test_write_string("\n");
}
test_summary(); test_summary();
test_shutdown(); test_shutdown();
} }
...@@ -18,11 +18,8 @@ void test_no_run() ...@@ -18,11 +18,8 @@ void test_no_run()
void test_scheduler_run_service() void test_scheduler_run_service()
{ {
testcase_begin("test_scheduler_run_service"); testcase_begin("test_scheduler_run_service");
auto service = Service_Function<0, test_run>(); Service service = &test_run;
auto service_no_run = Service_Function<1, test_no_run>(); Timer timer(service);
auto services = std::tie(service, service_no_run);
Timer timer;
timer.set_service(service);
timer.set_period(10); timer.set_period(10);
timer.set_num_periods(2); timer.set_num_periods(2);
auto event_sources = std::tie(timer); auto event_sources = std::tie(timer);
...@@ -30,7 +27,7 @@ void test_scheduler_run_service() ...@@ -30,7 +27,7 @@ void test_scheduler_run_service()
auto scheduler = Scheduler<1>(); auto scheduler = Scheduler<1>();
scheduler.fetch_events_timed(event_sources, 5); scheduler.fetch_events_timed(event_sources, 5);
scheduler.run_queue_service(services); scheduler.run_queue_service();
test_equal(test_run_var, 1); test_equal(test_run_var, 1);
test_equal(test_no_run_var, 0); test_equal(test_no_run_var, 0);
testcase_end(); testcase_end();
...@@ -47,21 +44,21 @@ void test() ...@@ -47,21 +44,21 @@ void test()
class source class source
{ {
service_id id; Service service;
time_base_t deadline; time_base_t deadline;
bool has_been_fetched; bool has_been_fetched;
public: public:
source(service_id i, time_base_t d) source(Service srv, time_base_t d)
{ {
id = i; service = srv;
deadline = d; deadline = d;
has_been_fetched = false; has_been_fetched = false;
} }
bool next_event(Event& e, __attribute__((unused)) time_base_t t) bool next_event(Event& e, __attribute__((unused)) time_base_t t)
{ {
if (!has_been_fetched) { if (!has_been_fetched) {
e.id = id; e.service = &service;
e.deadline = deadline; e.deadline = deadline;
has_been_fetched = true; has_been_fetched = true;
return true; return true;
...@@ -77,33 +74,33 @@ void test_scheduler_sort_queue() ...@@ -77,33 +74,33 @@ void test_scheduler_sort_queue()
{ {
testcase_begin("test_scheduler_sort_queue"); testcase_begin("test_scheduler_sort_queue");
auto scheduler = Scheduler<3>(); auto scheduler = Scheduler<3>();
source a(0, 0);
source b(1, 1); Service service0 = &test<0>;
source c(2, 2); Service service1 = &test<1>;
Service service2 = &test<2>;
source a(service0, 0);
source b(service1, 1);
source c(service2, 2);
auto event_sources = std::tie(a, b, c); auto event_sources = std::tie(a, b, c);
auto service0 = Service_Function<0, test<0> >();
auto service1 = Service_Function<1, test<1> >();
auto service2 = Service_Function<2, test<2> >();
auto services = std::tie(service0, service1, service2);
scheduler.fetch_events_timed(event_sources, 16); scheduler.fetch_events_timed(event_sources, 16);
test_var = 0; test_var = 0;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
scheduler.sort_earliest_first(); scheduler.sort_earliest_first();
scheduler.run_queue_service(services); scheduler.run_queue_service();
} }
test_equal(test_var, 3); test_equal(test_var, 3);
source a2(0, 2); source a2(service0, 2);
source b2(1, 1); source b2(service1, 1);
source c2(2, 0); source c2(service2, 0);
auto event_sources2 = std::tie(a2, b2, c2); auto event_sources2 = std::tie(a2, b2, c2);
scheduler.fetch_events_timed(event_sources2, 16); scheduler.fetch_events_timed(event_sources2, 16);
test_var = 0; test_var = 0;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
scheduler.sort_earliest_first(); scheduler.sort_earliest_first();
scheduler.run_queue_service(services); scheduler.run_queue_service();
} }
// only service0 works, services1,2 don't alter value // only service0 works, services1,2 don't alter value
// as they're run before service0 // as they're run before service0
......
...@@ -29,29 +29,16 @@ public: ...@@ -29,29 +29,16 @@ public:
void test_service_function_exec() void test_service_function_exec()
{ {
testcase_begin("test_service_function_exec"); testcase_begin("test_service_function_exec");
auto service = Service_Function<0, &test_function>(); Service service = &test_function;
service.exec(); service();
test_equal(count_function, 1); test_equal(count_function, 1);
testcase_end(); testcase_end();
} }
void test_service_class_exec()
{
testcase_begin("test_service_class_exec");
test_class test = test_class(1);
auto service = Service_Class<0, test_class, &test_class::member_function>(test);
// exec
service.exec();
// no third element to be fetched
test_equal(count_class_member_function, 1);
testcase_end();
}
void start() void start()
{ {
test_init(); test_init();
test_service_function_exec(); test_service_function_exec();
test_service_class_exec();
test_summary(); test_summary();
test_shutdown(); test_shutdown();
} }
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
using namespace libnux::vx; using namespace libnux::vx;
void foo() {}
void test_timer_next_event() void test_timer_next_event()
{ {
testcase_begin("timer.next_event"); testcase_begin("timer.next_event");
auto timer = Timer(); auto timer = Timer(&foo);
Event e; Event e;
time_type start = 1; time_type start = 1;
time_type repeat = 2; time_type repeat = 2;
...@@ -38,7 +40,7 @@ void test_timer_next_event() ...@@ -38,7 +40,7 @@ void test_timer_next_event()
void test_timer_miss_run() void test_timer_miss_run()
{ {
testcase_begin("timer.missed_count"); testcase_begin("timer.missed_count");
auto timer = Timer(); auto timer = Timer(&foo);
time_type start = 1; time_type start = 1;
time_type repeat = 2; time_type repeat = 2;
time_type stop = 4; time_type stop = 4;
......
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
using namespace libnux::vx; using namespace libnux::vx;
void foo() {}
void test_timer_oneshot_next_event() void test_timer_oneshot_next_event()
{ {
testcase_begin("test_timer_oneshot_wants_run"); testcase_begin("test_timer_oneshot_wants_run");
auto timer = TimerOneshot(); auto timer = TimerOneshot(&foo);
Event e; Event e;
time_type runtime = 10; time_type runtime = 10;
time_type window = 5; time_type window = 5;
......
...@@ -182,8 +182,7 @@ def build(bld): ...@@ -182,8 +182,7 @@ def build(bld):
source = ["src/nux_runtime/start.cpp", source = ["src/nux_runtime/start.cpp",
"src/nux_runtime/initdeinit.cpp", "src/nux_runtime/initdeinit.cpp",
"src/nux_runtime/cxa_pure_virtual.cpp", "src/nux_runtime/cxa_pure_virtual.cpp",
"src/nux_runtime/stack_guards.cpp", "src/nux_runtime/stack_guards.cpp"],
"src/nux_runtime/exception_handling.cpp"],
use = f"nux_inc_vx_v{chip_version_number}", use = f"nux_inc_vx_v{chip_version_number}",
env = env, env = env,
) )
......