From 7a14ffe2a0f849d5ad006706452486e94b446373 Mon Sep 17 00:00:00 2001
From: "Wesley W. Terpstra" <w.terpstra@gsi.de>
Date: Wed, 22 Feb 2012 14:29:38 +0000
Subject: [PATCH] Use the select lines for the low address bits of a sub-word
 access. Virtual wishbone devices are bigendian. Added code for both big and
 little -endian to master accesses. Currently, it always uses bigendian
 code-path.

---
 api/etherbone.h     |  1 +
 api/format/master.c | 20 ++++++++++++--------
 api/format/slave.c  | 43 ++++++++++++++++++++++++++++++-------------
 3 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/api/etherbone.h b/api/etherbone.h
index d7da75b..2dc171d 100644
--- a/api/etherbone.h
+++ b/api/etherbone.h
@@ -139,6 +139,7 @@ typedef struct eb_handler {
   
   eb_user_data_t data;
   
+  /* If these support sub-word access, it must be bigendian. */
   eb_status_t (*read) (eb_user_data_t, eb_address_t, eb_width_t, eb_data_t*);
   eb_status_t (*write)(eb_user_data_t, eb_address_t, eb_width_t, eb_data_t);
 } *eb_handler_t;
diff --git a/api/format/master.c b/api/format/master.c
index 3b84963..7ac2fde 100644
--- a/api/format/master.c
+++ b/api/format/master.c
@@ -232,7 +232,7 @@ void eb_device_flush(eb_device_t devicep) {
       eb_data_t wv;
       eb_operation_flags_t rcfg, wcfg;
       eb_width_t width;
-      uint8_t bus_alignment;
+      uint8_t op_shift, low_addr;
       
       scanp = operationp;
       
@@ -246,14 +246,14 @@ void eb_device_flush(eb_device_t devicep) {
         wcfg = 0;
         
         width = EB_DATAX;
-        bus_alignment = 0;
+        low_addr = 0;
       } else {
         wcfg = scan->flags & EB_OP_CFG_SPACE;
         bwa = scan->address;
         scanp = scan->next;
         
         width = scan->width;
-        bus_alignment = bwa & (data-1);
+        low_addr = bwa & (data-1);
         
         if (wcfg == 0) ++ops;
         
@@ -315,14 +315,14 @@ void eb_device_flush(eb_device_t devicep) {
       if (ops >= maxops ||
           scanp == EB_NULL ||
           ((scan = EB_OPERATION(scanp))->flags & EB_OP_MASK) == EB_OP_WRITE ||
-          (width != EB_DATAX && (scan->width != width || (scan->address & (data-1)) != bus_alignment))) {
+          (width != EB_DATAX && (scan->width != width || (scan->address & (data-1)) != low_addr))) {
         /* 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);
+        low_addr = scan->address & (data-1);
         if (rcfg == 0) ++ops;
         
         rcount = 1;
@@ -331,7 +331,7 @@ void eb_device_flush(eb_device_t devicep) {
           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 ((scan->address & (data-1)) != low_addr) break;
           if (rcount >= 255) break;
           if (ops >= maxops) break;
           if (rcfg == 0) ++ops;
@@ -401,6 +401,10 @@ void eb_device_flush(eb_device_t devicep) {
       cycle_end = 
         scanp == EB_NULL &&
         (!needs_check || ops == 0 || rxcount != rcount);
+        
+      /* The low address bits determine how far to shift values */
+      //op_shift = low_addr; /* Little-endian !!! -- must somehow decide if slave is big/little-endian */
+      op_shift = data - (low_addr+width); /* Big endian */
       
       /* Start by preparting the header */
       memset(wptr, 0, record_alignment);
@@ -409,7 +413,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] = (0xFF >> (8-width)) << bus_alignment;
+      wptr[1] = (0xFF >> (8-width)) << op_shift;
       wptr[2] = wcount;
       wptr[3] = rxcount;
       wptr += record_alignment;
@@ -425,7 +429,7 @@ void eb_device_flush(eb_device_t devicep) {
           operation = EB_OPERATION(operationp);
           
           wv = operation->write_value;
-          wv <<= (bus_alignment<<3);
+          wv <<= (op_shift<<3);
           
           EB_mWRITE(wptr, wv, alignment);
           wptr += alignment;
diff --git a/api/format/slave.c b/api/format/slave.c
index 5548ac1..44a6f05 100644
--- a/api/format/slave.c
+++ b/api/format/slave.c
@@ -70,7 +70,7 @@ void eb_device_slave(eb_socket_t socketp, eb_transport_t transportp, eb_device_t
   uint8_t* wptr, * rptr, * eos;
   uint64_t error;
   eb_width_t widths, biggest, data, addr;
-  eb_address_t address_check_bits;
+  eb_address_t address_filter_bits;
   int alignment, record_alignment, header_alignment, stride, cycle;
   int reply, header, passive, active;
   
@@ -180,9 +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);
+  /* Only these bits of incoming addresses are processed */
+  address_filter_bits = ~(eb_address_t)0;
+  address_filter_bits >>= (sizeof(eb_address_t) - addr) << 3;
+  address_filter_bits -= (data-1);
   
   /* Setup the initial pointers */
   wptr = &buffer[0];
@@ -199,11 +200,11 @@ resume_cycle:
 
   /* Start processing the payload */
   while (rptr <= eos - record_alignment) {
-    int total, wconfig, wfifo, rconfig, rfifo, bconfig, addr_ok, sel_ok;
+    int total, wconfig, wfifo, rconfig, rfifo, bconfig, sel_ok;
     eb_address_t bwa, bra, ra;
     eb_data_t wv, data_mask;
     eb_width_t op_width, op_widths;
-    uint8_t op_shift, bits, bits1;
+    uint8_t op_shift, addr_low, bits, bits1;
     uint8_t flags  = rptr[0];
     uint8_t select = rptr[1];
     uint8_t wcount = rptr[2];
@@ -232,6 +233,9 @@ resume_cycle:
           && op_width <= data            /* The width must be supported by the port */
           && op_shift < data;            /* The shift must be supported by the port */
     
+    /* Determine the low address bits of the operation */
+    addr_low = data - (op_shift+op_width); /* Big endian */
+    
     /* 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;
@@ -276,8 +280,14 @@ resume_cycle:
       bwa = EB_LOAD(rptr, alignment);
       rptr += alignment;
       
-      addr_ok = sel_ok && (bwa & address_check_bits) == (eb_address_t)op_shift;
-      
+      if (wconfig) {
+        /* Our config space uses all bits of the address */
+      } else {
+        /* Wishbone devices ignore the low address bits and use the select lines */
+        bwa &= address_filter_bits;
+        bwa |= addr_low; 
+      }
+        
       while (wcount--) {
         wv = EB_LOAD(rptr, alignment);
         rptr += alignment;
@@ -286,9 +296,10 @@ resume_cycle:
         wv &= data_mask;
         
         if (wconfig) {
-          eb_socket_write_config(socketp, op_width, bwa, wv);
+          if (sel_ok)
+            eb_socket_write_config(socketp, op_width, bwa, wv);
         } else {
-          if (addr_ok)
+          if (sel_ok)
             eb_socket_write(socketp, op_width, bwa, wv, &error);
           else
             error = (error<<1) | 1;
@@ -328,16 +339,22 @@ resume_cycle:
         ra = EB_LOAD(rptr, alignment);
         rptr += alignment;
         
-        addr_ok = sel_ok && (ra & address_check_bits) == (eb_address_t)op_shift;
+        if (rconfig) {
+          /* Our config space uses all bits of the address */
+        } else {
+          /* Wishbone devices ignore the low address bits and use the select lines */
+          ra &= address_filter_bits;
+          ra |= addr_low;
+        }
         
         if (rconfig) {
-          if (addr_ok) {
+          if (sel_ok) {
             wv = eb_socket_read_config(socketp, op_width, ra, error);
           } else {
             wv = 0;
           }
         } else {
-          if (addr_ok) {
+          if (sel_ok) {
             wv = eb_socket_read(socketp, op_width, ra, &error);
           } else {
             wv = 0;
-- 
GitLab