jim herd / FPGA_bus
Revision:
20:aacf2ebd93ff
Parent:
17:928b755cba80
Child:
21:6b2b7a0e2d9a
--- a/FPGA_bus.cpp	Mon May 25 14:14:17 2020 +0000
+++ b/FPGA_bus.cpp	Mon Jun 15 21:29:40 2020 +0000
@@ -15,6 +15,8 @@
  *      on the FPGA.
  */ 
  
+//////////////////////////////////////////////////////////////////////////
+
 FPGA_bus::FPGA_bus(int nos_PWM   /* = NOS_PWM_CHANNELS */, 
                    int nos_QE    /* = NOS_QE_CHANNELS  */, 
                    int nos_servo /* = NOS_RC_CHANNELS  */ )
@@ -39,11 +41,36 @@
         RC_base          = 0;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// do_reset : reset FPGA system
+// ========
+//
+// Generate a 20uS LOW going pulse to set FPGA into initial state
 
-void  FPGA_bus::initialise(void)
+void FPGA_bus:: do_reset(void)
 {
+    async_uP_reset = LOW;       // generate low reset pulse
+    wait_us(20);
+    async_uP_reset = HIGH;
+    wait_us(20);
+}
+ 
+//////////////////////////////////////////////////////////////////////////
+// initialise : Configure uP to FPGA data bus
+// ==========
+//
+// Do the following
+//  1. Set output signals to known values
+//  2. Prod FPGA to check that it is responding correctly
+//          Code  will make 3 attempts before returning with an ERROR    
+//  3. Read register ZERO to get number of units in FPGA
+//          This data is used to initialise the correct register pointers
 
-    update_FPGA_register_pointers();
+int32_t  FPGA_bus::initialise(void)
+{
+int32_t     status;
+
+    status = NO_ERROR;
     
     // GPIOC Periph clock enable
     
@@ -55,15 +82,33 @@
     async_uP_handshake_1 = LOW;
     async_uP_RW          = LOW;
     
-    async_uP_reset = LOW;       // generate low reset pulse
-    wait_us(20);
-    async_uP_reset = HIGH;
-    wait_us(20);
+    do_reset();
+//
+// Test to see if FPGA is alive and well
+//    
+    status = check_bus();
+    if (status != NO_ERROR) {
+        global_FPGA_unit_error_flag = status;
+        return status;
+    }  
+//
+// Seems OK, now read register 0 and get basic system parameters
+//
+    wait_ms(10);
+    get_SYS_data();
     
+    if (global_FPGA_unit_error_flag != NO_ERROR){
+        return global_FPGA_unit_error_flag;
+    }
     global_FPGA_unit_error_flag = NO_ERROR;
     log_pin = LOW;
+    return NO_ERROR;
 }
-    
+
+//////////////////////////////////////////////////////////////////////////  
+// do_start : generate signals to start of bus transaction 
+// ========
+
 void FPGA_bus::do_start(void)
 {
     async_uP_start       = HIGH;
@@ -71,6 +116,10 @@
     async_uP_start       = LOW;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// do_end : do actions to complete bus transaction
+// ======
+
 void FPGA_bus::do_end(void)
 {
     while (uP_ack == HIGH)
@@ -78,6 +127,10 @@
     async_uP_start = LOW;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// write_byte : write a byte of data to the FPGA
+// ==========
+
 void FPGA_bus::write_byte(uint32_t byte_value)
 {
     SET_BUS_OUTPUT;
@@ -91,6 +144,10 @@
         ;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// read_byte : read a byte of data from the FPGA
+// =========
+
 uint32_t FPGA_bus::read_byte(void)
 {
     SET_BUS_INPUT;
@@ -105,6 +162,10 @@
     return data;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// do write : write a SIX byte command to FPGA
+// ========
+
 void FPGA_bus::do_write(uint32_t command, 
               uint32_t register_address, 
               uint32_t register_data)
@@ -117,6 +178,10 @@
     write_byte(register_data>>24);
 }
 
+//////////////////////////////////////////////////////////////////////////
+// do_read : Read the results of FPGA command execution
+// =======
+
 void FPGA_bus::do_read(received_packet_t   *buffer)
 {
     for (int i=0; i < (NOS_RECEIVED_PACKET_WORDS<<2) ; i++) {
@@ -124,6 +189,10 @@
     }
 }
 
+//////////////////////////////////////////////////////////////////////////
+// do_transaction : execute complete command and get results
+// ==============
+
 void FPGA_bus::do_transaction(uint32_t command, 
                     uint32_t register_address, 
                     uint32_t register_data,
@@ -140,12 +209,10 @@
     *status = in_pkt.word_data[1];
 }
 
-uint32_t  FPGA_bus::do_command(FPGA_packet_t cmd_packet)
-{
-    async_uP_start = LOW;
-    return 0;
-}
-    
+////////////////////////////////////////////////////////////////////////// 
+// set_PWM_period : set PWM period given frequency
+// ==============
+   
 void FPGA_bus::set_PWM_period(uint32_t channel, float frequency)
 {
     uint32_t register_address = ((PWM_base + (channel * NOS_PWM_REGISTERS)) + PWM_PERIOD);
@@ -156,6 +223,10 @@
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// set_PWM_duty : set puty time at % of period
+// ============
+
 void FPGA_bus::set_PWM_duty(uint32_t channel, float percentage)
 {
     uint32_t register_address = ((PWM_base + (channel * NOS_PWM_REGISTERS)) + PWM_ON_TIME);
@@ -165,6 +236,10 @@
     global_FPGA_unit_error_flag = status;;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// PWM_enable : enable PWM output
+// ==========
+
 void FPGA_bus::PWM_enable(uint32_t channel)
 {
     uint32_t register_address = ((PWM_base + (channel * NOS_PWM_REGISTERS)) + PWM_CONFIG);
@@ -172,6 +247,10 @@
     global_FPGA_unit_error_flag = status;;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// PWM_config : program PWM configuration register
+// ==========
+
 void FPGA_bus::PWM_config(uint32_t channel, uint32_t config_value)
 {
     uint32_t register_address = ((PWM_base + (channel * NOS_PWM_REGISTERS)) + PWM_CONFIG);
@@ -180,12 +259,25 @@
     global_FPGA_unit_error_flag = status;;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// set_RC_period : set RC period to 20mS for all servos
+// =============
+//
+// No parameter therefore assume 20mS period
+//
+// FPGA clock = 20nS => 20mS = 1,000,000 clocks counts
+
 void FPGA_bus::set_RC_period(void)
 {
     do_transaction(WRITE_REGISTER_CMD, (RC_base + RC_SERVO_PERIOD), 1000000, &data, &status);
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// set_RC_period : set RC pulse in units of uS for all servos
+// =============
+//
+
 void FPGA_bus::set_RC_period(uint32_t duty_uS)
 {
     uint32_t nos_20nS_ticks = ((duty_uS * nS_IN_uS)/FPGA_CLOCK_PERIOD_nS);
@@ -193,6 +285,11 @@
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// set_RC_pulse : set RC pulse width in uS for a particular servo
+// ============
+//
+
 void FPGA_bus :: set_RC_pulse(uint32_t channel, uint32_t pulse_uS)
 {
     uint32_t nos_20nS_ticks = ((pulse_uS * nS_IN_uS)/FPGA_CLOCK_PERIOD_nS);
@@ -200,6 +297,10 @@
     global_FPGA_unit_error_flag = status;;    
 }
 
+//////////////////////////////////////////////////////////////////////////
+// enable_RC_channel : enable RC servo channel output
+// =================
+
 void FPGA_bus::enable_RC_channel(uint32_t channel)
 {
     do_transaction(READ_REGISTER_CMD, (RC_base + RC_SERVO_CONFIG), NULL, &data, &status);
@@ -208,6 +309,10 @@
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// disable_RC_channel : disable RC servo channel output
+// ==================
+
 void FPGA_bus::disable_RC_channel(uint32_t channel)
 {
     do_transaction(READ_REGISTER_CMD, (RC_base + RC_SERVO_CONFIG), NULL, &data, &status);
@@ -216,6 +321,10 @@
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// QE_config : configure quadrature encoder unit
+// =========
+
 void FPGA_bus::QE_config(uint32_t channel, uint32_t config_value)
 {
     uint32_t register_address = ((PWM_base + (channel * NOS_PWM_REGISTERS)) + PWM_CONFIG);
@@ -223,6 +332,10 @@
     global_FPGA_unit_error_flag = status;;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// enable_speed_measure : enable speed calculation of encoder channel
+// ====================
+
 void FPGA_bus::enable_speed_measure(uint32_t channel, uint32_t config_value, uint32_t phase_time)
 {
     uint32_t register_base = (QE_base + (channel * NOS_QE_REGISTERS));
@@ -235,6 +348,10 @@
     global_FPGA_unit_error_flag = status;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// read_speed_measure : read current speed measurement
+// ==================
+
 uint32_t FPGA_bus::read_speed_measure(uint32_t channel)
 {
     uint32_t register_address = ((QE_base + (channel * NOS_QE_REGISTERS)) + QE_SPEED_BUFFER);
@@ -243,6 +360,10 @@
     return data;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// read_count_measure : read encoder counter register
+// ==================
+
 uint32_t FPGA_bus::read_count_measure(uint32_t channel)
 {
     uint32_t register_address = ((QE_base + (channel * NOS_QE_REGISTERS)) + QE_COUNT_BUFFER);
@@ -251,22 +372,100 @@
     return data;
 }
 
+//////////////////////////////////////////////////////////////////////////
+// get_SYS_data : read system data from register 0
+// ============
+//
+// Notes
+//      Register data has information to define where the subsystem registers
+//      are located.  The software will therefore be independent of the 
+//      number of subsystems in the FPGA
+
 uint32_t FPGA_bus::get_SYS_data(void)
 {
     do_transaction(READ_REGISTER_CMD, SYS_DATA_REG_ADDR, NULL, &data, &status);
     sys_data.major_version          = (data & 0x0000000F);
-    sys_data.minor_version          = (data >> 4) & 0x0000000F;
-    sys_data.number_of_PWM_channels = (data >> 8) & 0x0000000F;
-    sys_data.number_of_QE_channels  = (data >> 8) & 0x0000000F;
-    sys_data.number_of_RC_channels  = (data >> 8) & 0x0000000F;
+    sys_data.minor_version          = (data >> 4)  & 0x0000000F;
+    sys_data.number_of_PWM_channels = (data >> 8)  & 0x0000000F;
+    sys_data.number_of_QE_channels  = (data >> 12) & 0x0000000F;
+    sys_data.number_of_RC_channels  = (data >> 16) & 0x0000000F;
+    
+    update_FPGA_register_pointers();
     
     global_FPGA_unit_error_flag = status;
     return data;
 }
+//////////////////////////////////////////////////////////////////////////
+// update_FPGA_register_pointers : set pointer to FPGA register bank
+// =============================
+//
+// Accessing the units within the FPGA is by reading and writing to registers.
+// This block of registers is a contiguous block starting at ZERO, up to
+// a maximum of 255.  This implies that if the FPGA is configured with more
+// PWM units (say), then the addresses of the later units are changed.  
+//
+// To overcome this problem, register ZERO contains a note of the number of
+// PWM, QE, abd RC servo units.  This address is fixed and is read to 
+// calculate the relevant register address pointers.
+//
 
 void FPGA_bus::update_FPGA_register_pointers(void) {
     
     PWM_base = 1;
-    QE_base  = ((NOS_PWM_REGISTERS * _nos_PWM_units) + PWM_base);
-    RC_base  = ((NOS_QE_REGISTERS * _nos_QE_units) + QE_base);   
-}
\ No newline at end of file
+    QE_base  = ((NOS_PWM_REGISTERS * sys_data.number_of_PWM_channels) + PWM_base);
+    RC_base  = ((NOS_QE_REGISTERS * sys_data.number_of_QE_channels) + QE_base);   
+}
+
+//////////////////////////////////////////////////////////////////////////
+// check_bus : verify that connection to FPGA is working
+// =========
+//
+// Notes
+//      Timeout on a first handshake after a RESET. Uses first part of
+//      'write_byte' handshake to test bus
+//      Three attempts are made before a fault is returned.
+// Return
+//      OK       = bus active and replying correctly
+//      BUS_FAIL = unable to get response from FPGA through bus
+//
+int32_t FPGA_bus::check_bus(void)
+{
+    
+    int32_t status = NO_ERROR;
+    for (uint32_t i=0 ; i < NOS_BUS_CHECKS ; i++) {
+        do_reset();
+        do_start();
+        SET_BUS_INPUT;
+        async_uP_RW = READ_BUS;
+        
+        SET_BUS_OUTPUT;
+        OUTPUT_BYTE_TO_BUS(0);
+        async_uP_RW = WRITE_BUS;
+        async_uP_handshake_1 = HIGH;
+        
+        
+        uint32_t timeout_count = BUS_TEST_TIMEOUT;
+        
+        while (uP_handshake_2 == LOW) {
+            timeout_count--;
+            if (timeout_count == 0) {
+                status = BUS_FAIL;
+                continue;           // try again
+            }
+        }
+        
+        status = NO_ERROR;
+        break;
+    }
+//
+// set system back to original state
+//
+    async_uP_start       = LOW;
+    async_uP_reset       = HIGH;
+    async_uP_handshake_1 = LOW;
+    async_uP_RW          = LOW;
+    do_reset();
+    return status;
+}
+
+    
\ No newline at end of file