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)
template <size_t N, class T>
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];
}
template <size_t N, class T>
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];
}
......
......@@ -25,15 +25,12 @@ public:
/**
* Scheduler loop.
* \tparam Signaller Scheduler loop control type
* \tparam S Service types
* \tparam E Event source types
* \param signaller Scheduler loop control instance
* \param services Tuple of references to services
* \param event_sources Tuple of references to event sources
*/
template <typename Signaller, typename... S, typename... E>
void execute(
Signaller& signaller, std::tuple<S&...>& services, std::tuple<E&...>& event_sources);
template <typename Signaller, typename... E>
void execute(Signaller& signaller, std::tuple<E&...>& event_sources);
/**
* Get maximal number of elements stored in queue since creation.
......@@ -67,11 +64,8 @@ public:
/**
* 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(std::tuple<S&...>& services);
void run_queue_service();
private:
/**
......@@ -81,10 +75,11 @@ private:
* \tparam Is Succeeding indices
* \param sources Tuple of references to event sources
* \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>
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.
......@@ -92,31 +87,11 @@ private:
* \tparam I Index
* \param sources Tuple of references to event sources
* \param t Time to fetch events for
* \param e Event object storage to use for fetching from timers
*/
template <typename... E, size_t I>
void fetch_events_from_source(std::tuple<E...>& sources, std::index_sequence<I>, time_type t);
/**
* 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);
void fetch_events_from_source(
std::tuple<E...>& sources, std::index_sequence<I>, time_type t, Event& e);
};
template <size_t queue_size>
......@@ -146,27 +121,26 @@ template <typename... E>
inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_timed(
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 <typename... E, size_t I, size_t... Is>
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)) {
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 <typename... E, size_t I>
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)) {
m_queue.push(e);
}
......@@ -175,58 +149,37 @@ inline __attribute__((always_inline)) void Scheduler<queue_size>::fetch_events_f
template <size_t queue_size>
inline __attribute__((always_inline)) void Scheduler<queue_size>::sort_earliest_first()
{
// linear search for earliest deadline
time_type earliest = m_queue[0].deadline;
size_t min_index = 0;
for (size_t i = 1; i < m_queue.get_size(); ++i) {
if (m_queue[i].deadline < earliest) {
min_index = i;
earliest = m_queue[i].deadline;
size_t const current_queue_size = m_queue.get_size();
if (current_queue_size > 1) {
// linear search for earliest deadline
time_type earliest = m_queue[0].deadline;
size_t min_index = 0;
for (size_t i = 1; i < current_queue_size; ++i) {
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 <typename... S>
inline __attribute__((always_inline)) void Scheduler<queue_size>::run_queue_service(
std::tuple<S&...>& services)
inline __attribute__((always_inline)) void Scheduler<queue_size>::run_queue_service()
{
Event e;
if (m_queue.pop(e)) {
run_service(services, std::make_index_sequence<sizeof...(S)>(), e.id);
}
}
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();
(*e.service)();
}
}
template <size_t queue_size>
template <typename Signaller, typename... S, typename... E>
void Scheduler<queue_size>::execute(
Signaller& signaller, std::tuple<S&...>& services, std::tuple<E&...>& event_sources)
template <typename Signaller, typename... E>
void Scheduler<queue_size>::execute(Signaller& signaller, std::tuple<E&...>& event_sources)
{
scheduler_signal signal = signaller.signal();
while (not(signal == scheduler_exit)) {
......@@ -236,7 +189,7 @@ void Scheduler<queue_size>::execute(
fetch_events_timed(event_sources, get_time());
}
sort_earliest_first();
run_queue_service(services);
run_queue_service();
}
}
signal = signaller.signal();
......
#pragma once
#include "libnux/scheduling/types.hpp"
#include <functional>
// Service is a template that can execute
// an arbitrary function or class member function
// 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)();
}
};
// Service is a function pointer to be executed
typedef std::function<void(void)> Service;
......@@ -11,22 +11,20 @@ class Timer
time_type m_period;
time_type m_start;
// Event to be run
service_id m_id;
Service m_service;
// debug
// count missed runs
time_type m_missed;
time_type m_last_run;
time_type m_end_run;
public:
Timer();
Timer(Service service);
// get next event, return true, if new event
bool next_event(Event& event, time_type t);
// set service id directly
void set_service_id(service_id id);
// set service id by service
template <class S>
void set_service(S& s);
void set_service(Service service);
// set timing
void set_first_deadline(time_type first);
time_type get_first_deadline() const;
......@@ -37,30 +35,27 @@ public:
time_type get_missed_count();
};
inline Timer::Timer()
inline Timer::Timer(Service service)
{
this->m_id = 0;
this->m_start = 0;
this->m_num_periods = 0;
this->m_period = 0;
this->m_last_run = 0;
this->m_end_run = 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;
}
template <class T>
void Timer::set_service(T& t)
{
this->m_id = t.id;
this->m_service = service;
}
inline void Timer::set_first_deadline(time_type 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
......@@ -71,28 +66,26 @@ inline time_type Timer::get_first_deadline() const
inline void Timer::set_num_periods(time_type num)
{
this->m_num_periods = num;
m_end_run = m_start + m_period * m_num_periods;
}
inline void Timer::set_period(time_type 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)
{
if (t >= this->m_start) {
if ((t -= this->m_start) < this->m_period * this->m_num_periods) {
if (t >= this->m_last_run) {
this->m_last_run += this->m_period;
while (this->m_last_run < t) {
++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;
}
if ((t >= m_last_run) && (t < m_end_run)) {
this->m_last_run += this->m_period;
while (this->m_last_run < t) {
++this->m_missed;
this->m_last_run += this->m_period;
}
event.service = &(this->m_service);
event.deadline = this->m_last_run;
return true;
}
return false;
}
......
#pragma once
#include "libnux/scheduling/Service.hpp"
#include "libnux/scheduling/types.hpp"
class TimerOneshot
......@@ -9,45 +10,45 @@ class TimerOneshot
// earliest start time = m_deadline - m_window
time_type m_window;
// Event to be run
service_id m_id;
Service m_service;
bool m_has_been_run;
public:
TimerOneshot();
TimerOneshot(time_type t);
TimerOneshot(time_type deadline, time_type window);
TimerOneshot(Service service);
TimerOneshot(Service service, time_type t);
TimerOneshot(Service service, time_type deadline, time_type window);
// get 'next' event, return true, if new event
bool next_event(Event& event, time_type t);
// set service id directly
void set_service_id(service_id id);
// set service id by service
template <class S>
void set_service(S& s);
// set service
void set_service(Service service);
// set timing
void set_deadline(time_type t);
void set_window(time_type window);
};
TimerOneshot::TimerOneshot()
TimerOneshot::TimerOneshot(Service service)
{
this->m_deadline = 0;
this->m_window = 0;
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_window = 0;
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_window = window;
this->m_has_been_run = false;
this->m_service = service;
}
void TimerOneshot::set_deadline(time_type t)
......@@ -62,21 +63,15 @@ void TimerOneshot::set_window(time_type t)
this->m_has_been_run = false;
}
void TimerOneshot::set_service_id(service_id id)
void TimerOneshot::set_service(Service service)
{
this->m_id = id;
}
template <class S>
void TimerOneshot::set_service(S& s)
{
this->m_id = s.id;
this->m_service = service;
}
bool TimerOneshot::next_event(Event& event, time_type t)
{
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;
this->m_has_been_run = true;
return true;
......
#pragma once
#include "libnux/scheduling/Service.hpp"
#include "libnux/vx/spr.h"
#include <stdint.h>
typedef uint8_t service_id;
#ifdef LIBNUX_TIME64
typedef uint64_t time_type;
#else
......@@ -17,7 +16,7 @@ inline time_type get_time()
struct Event
{
service_id id = 0;
Service const* service;
time_type deadline;
};
......
......@@ -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_randgen_base = 0x000e0000;
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;
// clang-format on
......@@ -81,6 +82,7 @@ static constexpr omnibus_address_t cadc_top_acausal_base_address =
/* External memory base address for scalar data access */
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_bottom_base_address = extmem_data_base | (1ul << 28) | 0x4000;
......
......@@ -158,20 +158,21 @@ public:
/** ExtMem address type representing a "external memory" address. */
class ExtMem
template <uint32_t ScalarBaseAddress, uint32_t VectorBaseAddress, size_t WordSize>
class ExtMemBase
{
public:
/** Convert to "vector unit" address. */
constexpr byte_address to_vector_addr() const
{
return (m_ptr >> 2) | dls_extmem_base;
return (m_ptr >> 2) | VectorBaseAddress;
}
/** Convert to "vector fxvoutx" address. */
constexpr byte_address to_fxviox_addr() const
{
// Whatever...
return (m_ptr >> 4) | dls_extmem_base;
return (m_ptr >> 4) | VectorBaseAddress;
}
/** Convert to "vector unit" pointer. */
......@@ -193,7 +194,7 @@ public:
/** Convert to "scalar unit" address. */
constexpr byte_address to_scalar_addr() const
{
return m_ptr | extmem_data_base;
return m_ptr | ScalarBaseAddress;
}
/** Convert to "scalar unit" pointer. */
......@@ -208,11 +209,13 @@ public:
private:
friend GlobalAddress;
inline constexpr ExtMem(omnibus_address address, byte_address offset);
inline constexpr ExtMemBase(omnibus_address address, byte_address offset);
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. */
class SRAM
......@@ -317,6 +320,16 @@ public:
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. */
inline constexpr SRAM to_sram() const;
constexpr explicit operator SRAM() const
......@@ -334,6 +347,9 @@ public:
/** Check if global address represents a valid address in extmem. */
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. */
inline constexpr bool is_sram() const;
......@@ -396,22 +412,31 @@ struct AddressSpace
static_assert(vecgen_top.to_global_omnibus() == 0x8400'0000);
static_assert(vecgen_bot.to_global_omnibus() == 0x8400'1000);
/** External memory (FPGA-connected DRAM). */
/** External memory (FPGA-connected SRAM). */
GlobalAddress static constexpr extmem =
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);
/** 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) :
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
......@@ -511,6 +536,15 @@ constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::ExtMem>(
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 <>
constexpr GlobalAddress GlobalAddress::from_relative<GlobalAddress::SRAM>(
GlobalAddress::byte_address offset, PPUOnDLS me)
......@@ -541,6 +575,12 @@ constexpr bool GlobalAddress::is_extmem() const
(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
{
return (m_addr >= AddressSpace::sram_top.to_global_omnibus()) &&
......
......@@ -20,7 +20,7 @@ void sleep_cycles(uint32_t cycles);
because otherwise, mfsprs might get grouped together,
rendering measurement userless.
*/
time_base_t now();
time_base_t now() ATTRIB_LINK_TO_INTERNAL;
/*
Pair of time values.
......
......@@ -2,6 +2,8 @@ MEMORY {
int_mem(rwx) : ORIGIN = 0x00000000, LENGTH = 16K
ext_mem(rwx) : ORIGIN = 0x80000000, LENGTH = 128K
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 */
......@@ -119,6 +121,29 @@ SECTIONS {
/* global symbol marking the end of ext_mem_data data sections */
_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;
. = 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 @@
using namespace libnux::vx;
uint32_t data_ext __attribute__((section("ext.data")));
uint32_t data_ext_dram __attribute__((section("ext_dram.data")));
uint32_t data_int;
void start()
......@@ -12,6 +13,8 @@ void start()
testcase_begin("data placement");
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_ext_dram) & extmem_dram_data_base);
test_true(!(reinterpret_cast<uint32_t>(&data_int) & extmem_dram_data_base));
testcase_end();
test_summary();
test_shutdown();
......
......@@ -6,10 +6,10 @@
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)
{
T* ptr = (T*) (extmem_data_base + offset * sizeof(T));
T* ptr = (T*) (BaseAddress + offset * sizeof(T));
*ptr = value;
// cf. issue #3739
asm volatile("nop");
......@@ -27,6 +27,14 @@ void start(void)
test_write_read<uint8_t>(17 + 4, 0x20000 - 1); // w/r highest extmem address
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_shutdown();
}
......@@ -10,22 +10,43 @@ using namespace libnux::vx;
void start(void) {
test_init();
testcase_begin("external memory write read via vector unit");
vector_type values;
for (size_t entry = 0; entry < dls_vector_size; ++entry) {
values[entry] = entry;
{
testcase_begin("external 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);
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) {
test_equal(read[column], values[column]);
for (size_t column = 0; column < 128; ++column) {
test_equal(read[column], values[column]);
}
testcase_end();
}
testcase_end();
test_summary();
test_shutdown();
......
......@@ -102,6 +102,90 @@ void start()
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_shutdown();
}
......@@ -18,11 +18,8 @@ void test_no_run()
void test_scheduler_run_service()
{
testcase_begin("test_scheduler_run_service");
auto service = Service_Function<0, test_run>();
auto service_no_run = Service_Function<1, test_no_run>();
auto services = std::tie(service, service_no_run);
Timer timer;
timer.set_service(service);
Service service = &test_run;
Timer timer(service);
timer.set_period(10);
timer.set_num_periods(2);
auto event_sources = std::tie(timer);
......@@ -30,7 +27,7 @@ void test_scheduler_run_service()
auto scheduler = Scheduler<1>();
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_no_run_var, 0);
testcase_end();
......@@ -47,21 +44,21 @@ void test()
class source
{
service_id id;
Service service;
time_base_t deadline;
bool has_been_fetched;
public:
source(service_id i, time_base_t d)
source(Service srv, time_base_t d)
{
id = i;
service = srv;
deadline = d;
has_been_fetched = false;
}
bool next_event(Event& e, __attribute__((unused)) time_base_t t)
{
if (!has_been_fetched) {
e.id = id;
e.service = &service;
e.deadline = deadline;
has_been_fetched = true;
return true;
......@@ -77,33 +74,33 @@ void test_scheduler_sort_queue()
{
testcase_begin("test_scheduler_sort_queue");
auto scheduler = Scheduler<3>();
source a(0, 0);
source b(1, 1);
source c(2, 2);
Service service0 = &test<0>;
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 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);
test_var = 0;
for (int i = 0; i < 3; ++i) {
scheduler.sort_earliest_first();
scheduler.run_queue_service(services);
scheduler.run_queue_service();
}
test_equal(test_var, 3);
source a2(0, 2);
source b2(1, 1);
source c2(2, 0);
source a2(service0, 2);
source b2(service1, 1);
source c2(service2, 0);
auto event_sources2 = std::tie(a2, b2, c2);
scheduler.fetch_events_timed(event_sources2, 16);
test_var = 0;
for (int i = 0; i < 3; ++i) {
scheduler.sort_earliest_first();
scheduler.run_queue_service(services);
scheduler.run_queue_service();
}
// only service0 works, services1,2 don't alter value
// as they're run before service0
......
......@@ -29,29 +29,16 @@ public:
void test_service_function_exec()
{
testcase_begin("test_service_function_exec");
auto service = Service_Function<0, &test_function>();
service.exec();
Service service = &test_function;
service();
test_equal(count_function, 1);
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()
{
test_init();
test_service_function_exec();
test_service_class_exec();
test_summary();
test_shutdown();
}
......@@ -3,10 +3,12 @@
using namespace libnux::vx;
void foo() {}
void test_timer_next_event()
{
testcase_begin("timer.next_event");
auto timer = Timer();
auto timer = Timer(&foo);
Event e;
time_type start = 1;
time_type repeat = 2;
......@@ -38,7 +40,7 @@ void test_timer_next_event()
void test_timer_miss_run()
{
testcase_begin("timer.missed_count");
auto timer = Timer();
auto timer = Timer(&foo);
time_type start = 1;
time_type repeat = 2;
time_type stop = 4;
......
......@@ -3,10 +3,12 @@
using namespace libnux::vx;
void foo() {}
void test_timer_oneshot_next_event()
{
testcase_begin("test_timer_oneshot_wants_run");
auto timer = TimerOneshot();
auto timer = TimerOneshot(&foo);
Event e;
time_type runtime = 10;
time_type window = 5;
......
......@@ -182,8 +182,7 @@ def build(bld):
source = ["src/nux_runtime/start.cpp",
"src/nux_runtime/initdeinit.cpp",
"src/nux_runtime/cxa_pure_virtual.cpp",
"src/nux_runtime/stack_guards.cpp",
"src/nux_runtime/exception_handling.cpp"],
"src/nux_runtime/stack_guards.cpp"],
use = f"nux_inc_vx_v{chip_version_number}",
env = env,
)
......