Skip to content
Snippets Groups Projects
Commit bf0931d9 authored by Wesley W. Terpstra's avatar Wesley W. Terpstra
Browse files

Implement sub-word access

parent 44a79f80
No related merge requests found
......@@ -35,6 +35,7 @@
#include "../glue/cycle.h"
#include "../glue/device.h"
#include "../glue/socket.h"
#include "../glue/widths.h"
#include "../transport/transport.h"
#include "../memory/memory.h"
#include "format.h"
......@@ -62,8 +63,7 @@ void eb_device_flush(eb_device_t devicep) {
struct eb_response* response;
eb_cycle_t cyclep, nextp, prevp;
eb_response_t responsep;
eb_width_t biggest, data, width;
eb_data_t data_mask;
eb_width_t biggest, data, addr, width;
eb_address_t address_mask;
uint8_t buffer[sizeof(eb_max_align_t)*(255+255+1+1)+8]; /* big enough for worst-case record */
uint8_t * wptr, * cptr, * eob;
......@@ -78,15 +78,10 @@ void eb_device_flush(eb_device_t devicep) {
assert (eb_width_refined(width) != 0);
*/
/* Determine alignment and masking sets */
data_mask = ((eb_data_t)1) << (((width&EB_DATAX)<<3)-1);
data_mask = (data_mask-1) << 1 | 1;
address_mask = ((eb_address_t)1) << (((width&EB_ADDRX)>>1)-1);
address_mask = (address_mask-1) << 1 | 1;
/* Calculate alignment values */
data = width & EB_DATAX;
biggest = (width >> 4) | data;
addr = width >> 4;
biggest = addr | data;
alignment = 2;
alignment += (biggest >= EB_DATA32)*2;
alignment += (biggest >= EB_DATA64)*4;
......@@ -95,6 +90,10 @@ void eb_device_flush(eb_device_t devicep) {
header_alignment = record_alignment;
stride = data;
/* Determine alignment and masking sets */
address_mask = ~(eb_address_t)0;
address_mask >>= (sizeof(eb_address_t) - addr) << 3;
/* Non-streaming sockets need a header */
mtu = eb_transports[transport->link_type].mtu;
if (mtu != 0) {
......@@ -124,6 +123,8 @@ void eb_device_flush(eb_device_t devicep) {
struct eb_operation* scan;
eb_operation_t operationp;
eb_operation_t scanp;
eb_width_t width;
eb_data_t data_mask;
int needs_check, cycle_end;
unsigned int ops, maxops;
eb_status_t reason;
......@@ -151,23 +152,37 @@ void eb_device_flush(eb_device_t devicep) {
reason = EB_OK; /* silence warning */
for (operationp = cycle->first; operationp != EB_NULL; operationp = operation->next) {
operation = EB_OPERATION(operationp);
/* Determine operation port width: max of possibilities <= data */
width = eb_width_refine(operation->width & (data-1+data));
if (width == 0) {
reason = EB_WIDTH;
break;
}
/* Report what the operation width ended up to be */
operation->width = width;
/* Is the address too big for a bus op? */
if ((operation->flags & EB_OP_CFG_SPACE) == 0 &&
(operation->address & address_mask) != operation->address) {
(operation->address & (address_mask - (width - 1))) != operation->address) {
reason = EB_ADDRESS;
break;
}
/* Is the address too big for a cfg op? */
if ((operation->flags & EB_OP_CFG_SPACE) != 0 &&
(operation->address & 0xFFFFU) != operation->address) {
(operation->address & (0xFFFFU - (width - 1))) != operation->address) {
reason = EB_ADDRESS;
break;
}
/* Is the data too big for the port? */
if ((operation->flags & EB_OP_MASK) == EB_OP_WRITE &&
(operation->write_value & data_mask) != operation->write_value) {
reason = EB_WIDTH;
break;
if ((operation->flags & EB_OP_MASK) == EB_OP_WRITE) {
data_mask = ~(eb_data_t)0;
data_mask >>= (sizeof(eb_data_t) - width) << 3;
if ((operation->write_value & data_mask) != operation->write_value) {
reason = EB_WIDTH;
break;
}
}
}
if (operationp != EB_NULL) {
......@@ -214,7 +229,10 @@ void eb_device_flush(eb_device_t devicep) {
while (!cycle_end) {
int wcount, rcount, rxcount, total, length, fifo;
eb_address_t bwa;
eb_data_t wv;
eb_operation_flags_t rcfg, wcfg;
eb_width_t width;
uint8_t bus_alignment;
scanp = operationp;
......@@ -226,18 +244,25 @@ void eb_device_flush(eb_device_t devicep) {
wcount = 0;
fifo = 0;
wcfg = 0;
width = EB_DATAX;
bus_alignment = 0;
} else {
wcfg = scan->flags & EB_OP_CFG_SPACE;
bwa = scan->address;
scanp = scan->next;
width = scan->width;
bus_alignment = bwa & (data-1);
if (wcfg == 0) ++ops;
/* How many writes can we chain? must be either FIFO or sequential in same address space */
if (ops >= maxops ||
scanp == EB_NULL ||
((scan = EB_OPERATION(scanp))->flags & EB_OP_MASK) != EB_OP_WRITE ||
(scan->flags & EB_OP_CFG_SPACE) != wcfg) {
(scan->flags & EB_OP_CFG_SPACE) != wcfg ||
scan->width != width) {
/* Only a single write */
fifo = 0;
wcount = 1;
......@@ -254,6 +279,7 @@ void eb_device_flush(eb_device_t devicep) {
if (scan->address != bwa) break;
if ((scan->flags & EB_OP_MASK) != EB_OP_WRITE) break;
if ((scan->flags & EB_OP_CFG_SPACE) != wcfg) break;
if (scan->width != width) break;
if (wcount >= 255) break;
if (ops >= maxops) break;
if (wcfg == 0) ++ops;
......@@ -270,6 +296,7 @@ void eb_device_flush(eb_device_t devicep) {
if (scan->address != (bwa += stride)) break;
if ((scan->flags & EB_OP_MASK) != EB_OP_WRITE) break;
if ((scan->flags & EB_OP_CFG_SPACE) != wcfg) break;
if (scan->width != width) break;
if (wcount >= 255) break;
if (ops >= maxops) break;
if (wcfg == 0) ++ops;
......@@ -287,12 +314,15 @@ void eb_device_flush(eb_device_t devicep) {
/* First pack writes into a record, if any */
if (ops >= maxops ||
scanp == EB_NULL ||
((scan = EB_OPERATION(scanp))->flags & EB_OP_MASK) == EB_OP_WRITE) {
((scan = EB_OPERATION(scanp))->flags & EB_OP_MASK) == EB_OP_WRITE ||
(width != EB_DATAX && (scan->width != width || (scan->address & (data-1)) != bus_alignment))) {
/* No reads in this record */
rcount = 0;
rcfg = 0;
} else {
rcfg = scan->flags & EB_OP_CFG_SPACE;
width = scan->width;
bus_alignment = scan->address & (data-1);
if (rcfg == 0) ++ops;
rcount = 1;
......@@ -300,6 +330,8 @@ void eb_device_flush(eb_device_t devicep) {
scan = EB_OPERATION(scanp);
if ((scan->flags & EB_OP_MASK) == EB_OP_WRITE) break;
if ((scan->flags & EB_OP_CFG_SPACE) != rcfg) break;
if (scan->width != width) break;
if ((scan->address & (data-1)) != bus_alignment) break;
if (rcount >= 255) break;
if (ops >= maxops) break;
if (rcfg == 0) ++ops;
......@@ -307,8 +339,9 @@ void eb_device_flush(eb_device_t devicep) {
}
}
if (rcount == 0 && (ops >= maxops || (scanp == EB_NULL && needs_check && ops > 0))) {
if (rcount == 0 && width >= data && (ops >= maxops || (scanp == EB_NULL && needs_check && ops > 0))) {
/* Insert error-flag read */
width = data;
rxcount = 1;
rcfg = 1;
} else {
......@@ -376,7 +409,7 @@ void eb_device_flush(eb_device_t devicep) {
(wcfg ? EB_RECORD_WCA : 0) |
(fifo ? EB_RECORD_WFF : 0) |
(cycle_end ? EB_RECORD_CYC : 0);
wptr[1] = 0;
wptr[1] = (0xFF >> (8-width)) << bus_alignment;
wptr[2] = wcount;
wptr[3] = rxcount;
wptr += record_alignment;
......@@ -391,7 +424,10 @@ void eb_device_flush(eb_device_t devicep) {
for (; wcount--; operationp = operation->next) {
operation = EB_OPERATION(operationp);
EB_mWRITE(wptr, operation->write_value, alignment);
wv = operation->write_value;
wv <<= (bus_alignment<<3);
EB_mWRITE(wptr, wv, alignment);
wptr += alignment;
}
}
......
......@@ -55,6 +55,10 @@ static inline void EB_sWRITE(uint8_t* wptr, eb_data_t val, int alignment) {
}
}
/* Find the offset */
static uint8_t eb_log2_table[8] = { 0, 1, 2, 4, 7, 3, 6, 5 };
static inline uint8_t eb_log2(uint8_t x) { return eb_log2_table[(uint8_t)(x * 0x17) >> 5]; }
void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t devicep) {
struct eb_socket* socket;
struct eb_transport* transport;
......@@ -65,11 +69,8 @@ void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t
uint8_t buffer[sizeof(eb_max_align_t)*(255+255+1+1)+8]; /* big enough for worst-case record */
uint8_t* wptr, * rptr, * eos;
uint64_t error;
eb_width_t widths, biggest, data;
#ifdef ANAL
eb_data_t data_mask;
eb_address_t address_mask;
#endif
eb_width_t widths, biggest, data, addr;
eb_address_t address_check_bits;
int alignment, record_alignment, header_alignment, stride, cycle;
int reply, header, passive, active;
......@@ -84,7 +85,7 @@ void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t
link = EB_LINK(linkp);
widths = device->widths;
header = widths == 0;
header = widths == 0;
passive = device->passive == devicep;
active = !passive;
......@@ -166,17 +167,10 @@ void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t
if (!eb_width_possible(widths)) goto kill;
}
#ifdef ANAL
/* Determine alignment and masking sets */
data_mask = ((eb_data_t)1) << (((widths&EB_DATAX)<<3)-1);
data_mask = (data_mask-1) << 1 | 1;
address_mask = ((eb_address_t)1) << (((widths&EB_ADDRX)>>1)-1);
address_mask = (address_mask-1) << 1 | 1;
#endif
/* Alignment is either 2, 4, or 8. */
data = widths & EB_DATAX;
biggest = (widths >> 4) | data;
addr = widths >> 4;
biggest = addr | data;
alignment = 2;
alignment += (biggest >= EB_DATA32)*2;
alignment += (biggest >= EB_DATA64)*4;
......@@ -186,6 +180,10 @@ void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t
/* FIFO stride size */
stride = data;
address_check_bits = ~(eb_address_t)0;
address_check_bits >>= (sizeof(eb_address_t) - addr) << 3;
address_check_bits = ~address_check_bits | (data-1);
/* Setup the initial pointers */
wptr = &buffer[0];
if (header) wptr += header_alignment;
......@@ -201,15 +199,43 @@ resume_cycle:
/* Start processing the payload */
while (rptr <= eos - record_alignment) {
int total, wconfig, wfifo, rconfig, rfifo, bconfig;
int total, wconfig, wfifo, rconfig, rfifo, bconfig, addr_ok, sel_ok;
eb_address_t bwa, bra, ra;
eb_data_t wv;
eb_data_t wv, data_mask;
eb_width_t op_width, op_widths;
uint8_t op_shift, bits, bits1;
uint8_t flags = rptr[0];
uint8_t select = rptr[1];
uint8_t wcount = rptr[2];
uint8_t rcount = rptr[3];
rptr += record_alignment;
/* Decode the intended width from the select lines */
/* Step 1. How many bytes shifted are the operations?
* op_shift = position of first set bit
*/
op_shift = eb_log2(select & -select);
/* Step 2. How many ones are there in a row? */
bits = select >> op_shift;
bits1 = (bits>>1)+1;
op_widths = eb_log2(bits1);
op_width = op_widths+1;
/* Step 3. Check that the bitmask is valid */
sel_ok = select != 0 /* select is not 0 */
&& (bits & (bits+1)) == 0 /* One bit set => was an unbroken sequence of bits */
&& (op_width & op_widths) == 0 /* One bit set => operation length was a power of two */
&& (op_shift & op_widths) == 0 /* The operation is aligned to the width */
&& op_width <= data /* The width must be supported by the port */
&& op_shift < data; /* The shift must be supported by the port */
/* Create a mask for filtering out the important write data */
data_mask = ~(eb_data_t)0;
data_mask >>= (sizeof(eb_data_t) - op_width) << 3;
/* Is the cycle flag high? */
cycle = flags & EB_RECORD_CYC;
......@@ -248,21 +274,26 @@ resume_cycle:
wconfig = flags & EB_RECORD_WCA;
bwa = EB_LOAD(rptr, alignment);
#ifdef ANAL
if ((bwa & address_mask) != 0) goto fail;
#endif
rptr += alignment;
addr_ok = sel_ok && (bwa & address_check_bits) == (eb_address_t)op_shift;
while (wcount--) {
wv = EB_LOAD(rptr, alignment);
rptr += alignment;
#ifdef ANAL
if ((wv & data_mask) != 0) goto fail;
#endif
if (wconfig)
eb_socket_write_config(socketp, widths, bwa, wv);
else
eb_socket_write(socketp, widths, bwa, wv, &error);
wv >>= (op_shift<<3);
wv &= data_mask;
if (wconfig) {
eb_socket_write_config(socketp, op_width, bwa, wv);
} else {
if (addr_ok)
eb_socket_write(socketp, op_width, bwa, wv, &error);
else
error = (error<<1) | 1;
}
if (wfifo == 0) bwa += stride;
}
}
......@@ -280,16 +311,13 @@ resume_cycle:
wptr[0] = cycle |
(bconfig ? EB_RECORD_WCA : 0) |
(rfifo ? EB_RECORD_WFF : 0);
wptr[1] = 0;
wptr[1] = select;
wptr[2] = rcount;
wptr[3] = 0;
wptr += record_alignment;
bra = EB_LOAD(rptr, alignment);
#ifdef ANAL
if ((bra & address_mask) != 0) goto fail;
#endif
rptr += alignment;
/* Echo back the base return address */
......@@ -299,13 +327,26 @@ resume_cycle:
while (rcount--) {
ra = EB_LOAD(rptr, alignment);
rptr += alignment;
#ifdef ANAL
if ((ra & address_mask) != 0) goto fail;
#endif
if (rconfig)
wv = eb_socket_read_config(socketp, widths, ra, error);
else
wv = eb_socket_read(socketp, widths, ra, &error);
addr_ok = sel_ok && (ra & address_check_bits) == (eb_address_t)op_shift;
if (rconfig) {
if (addr_ok) {
wv = eb_socket_read_config(socketp, op_width, ra, error);
} else {
wv = 0;
}
} else {
if (addr_ok) {
wv = eb_socket_read(socketp, op_width, ra, &error);
} else {
wv = 0;
error = (error<<1) | 1;
}
}
wv &= data_mask;
wv <<= (op_shift<<3);
EB_sWRITE(wptr, wv, alignment);
wptr += alignment;
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment