nds32: support Andes profiling function
authorHsiangkai Wang <hsiangkai@gmail.com>
Tue, 27 Aug 2013 08:10:16 +0000 (16:10 +0800)
committerSpencer Oliver <spen@spen-soft.co.uk>
Fri, 13 Sep 2013 19:37:23 +0000 (19:37 +0000)
Change-Id: Ibc45ec5777d6841956c02de6b4ae8e74c2a6de37
Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com>
Reviewed-on: http://openocd.zylin.com/1585
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
src/jtag/aice/aice_port.h
src/jtag/aice/aice_usb.c
src/target/nds32.c
src/target/nds32.h
src/target/nds32_v3.c

index 8f3e855129d5d90b102b1867e6069ba438d5383e..35bc61c9d966f56b8a2d308e59c0de1762842f43 100644 (file)
@@ -218,6 +218,10 @@ struct aice_port_api_s {
 
        /** */
        int (*set_data_endian)(enum aice_target_endian target_data_endian);
+
+       /** */
+       int (*profiling)(uint32_t interval, uint32_t iteration,
+               uint32_t reg_no, uint32_t *samples, uint32_t *num_samples);
 };
 
 #define AICE_PORT_UNKNOWN      0
index 30509d3b2c03d1d8c6f96204797fe5e75cff56dc..f25a0f5ce999b2a9b5a203b0924491846fb7ec71 100644 (file)
@@ -390,7 +390,7 @@ static uint32_t usb_out_packets_buffer_length;
 static uint32_t usb_in_packets_buffer_length;
 static enum aice_command_mode aice_command_mode;
 
-extern int aice_batch_buffer_write(uint8_t buf_index, const uint8_t *word,
+static int aice_batch_buffer_write(uint8_t buf_index, const uint8_t *word,
                uint32_t num_of_words);
 
 static int aice_usb_packet_flush(void)
@@ -3788,6 +3788,168 @@ static int aice_usb_set_data_endian(enum aice_target_endian target_data_endian)
        return ERROR_OK;
 }
 
+static int fill_profiling_batch_commands(uint32_t reg_no)
+{
+       uint32_t dim_instructions[4];
+
+       aice_usb_set_command_mode(AICE_COMMAND_MODE_BATCH);
+
+       /* halt */
+       if (aice_write_misc(current_target_id, NDS_EDM_MISC_EDM_CMDR, 0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* backup $r0 */
+       dim_instructions[0] = MTSR_DTR(0);
+       dim_instructions[1] = DSB;
+       dim_instructions[2] = NOP;
+       dim_instructions[3] = BEQ_MINUS_12;
+       if (aice_write_dim(current_target_id, dim_instructions, 4) != ERROR_OK)
+               return ERROR_FAIL;
+       aice_read_dtr_to_buffer(current_target_id, AICE_BATCH_DATA_BUFFER_0);
+
+       /* get samples */
+       if (NDS32_REG_TYPE_GPR == nds32_reg_type(reg_no)) {
+               /* general registers */
+               dim_instructions[0] = MTSR_DTR(reg_no);
+               dim_instructions[1] = DSB;
+               dim_instructions[2] = NOP;
+               dim_instructions[3] = BEQ_MINUS_12;
+       } else if (NDS32_REG_TYPE_SPR == nds32_reg_type(reg_no)) {
+               /* user special registers */
+               dim_instructions[0] = MFUSR_G0(0, nds32_reg_sr_index(reg_no));
+               dim_instructions[1] = MTSR_DTR(0);
+               dim_instructions[2] = DSB;
+               dim_instructions[3] = BEQ_MINUS_12;
+       } else { /* system registers */
+               dim_instructions[0] = MFSR(0, nds32_reg_sr_index(reg_no));
+               dim_instructions[1] = MTSR_DTR(0);
+               dim_instructions[2] = DSB;
+               dim_instructions[3] = BEQ_MINUS_12;
+       }
+       if (aice_write_dim(current_target_id, dim_instructions, 4) != ERROR_OK)
+               return ERROR_FAIL;
+       aice_read_dtr_to_buffer(current_target_id, AICE_BATCH_DATA_BUFFER_1);
+
+       /* restore $r0 */
+       aice_write_dtr_from_buffer(current_target_id, AICE_BATCH_DATA_BUFFER_0);
+       dim_instructions[0] = MFSR_DTR(0);
+       dim_instructions[1] = DSB;
+       dim_instructions[2] = NOP;
+       dim_instructions[3] = IRET;  /* free run */
+       if (aice_write_dim(current_target_id, dim_instructions, 4) != ERROR_OK)
+               return ERROR_FAIL;
+
+       aice_command_mode = AICE_COMMAND_MODE_NORMAL;
+
+       /* use BATCH_BUFFER_WRITE to fill command-batch-buffer */
+       if (aice_batch_buffer_write(AICE_BATCH_COMMAND_BUFFER_0,
+                               usb_out_packets_buffer,
+                               (usb_out_packets_buffer_length + 3) / 4) != ERROR_OK)
+               return ERROR_FAIL;
+
+       usb_out_packets_buffer_length = 0;
+       usb_in_packets_buffer_length = 0;
+
+       return ERROR_OK;
+}
+
+static int aice_usb_profiling(uint32_t interval, uint32_t iteration,
+               uint32_t reg_no, uint32_t *samples, uint32_t *num_samples)
+{
+       uint32_t iteration_count;
+       uint32_t this_iteration;
+       int retval = ERROR_OK;
+       const uint32_t MAX_ITERATION = 250;
+
+       *num_samples = 0;
+
+       /* init DIM size */
+       if (aice_write_ctrl(AICE_WRITE_CTRL_BATCH_DIM_SIZE, 4) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Use AICE_BATCH_DATA_BUFFER_0 to read/write $DTR.
+        * Set it to circular buffer */
+       if (aice_write_ctrl(AICE_WRITE_CTRL_BATCH_DATA_BUF0_CTRL, 0xC0000) != ERROR_OK)
+               return ERROR_FAIL;
+
+       fill_profiling_batch_commands(reg_no);
+
+       iteration_count = 0;
+       while (iteration_count < iteration) {
+               if (iteration - iteration_count < MAX_ITERATION)
+                       this_iteration = iteration - iteration_count;
+               else
+                       this_iteration = MAX_ITERATION;
+
+               /* set number of iterations */
+               uint32_t val_iteration;
+               val_iteration = interval << 16 | this_iteration;
+               if (aice_write_ctrl(AICE_WRITE_CTRL_BATCH_ITERATION,
+                                       val_iteration) != ERROR_OK) {
+                       retval = ERROR_FAIL;
+                       goto end_profiling;
+               }
+
+               /* init AICE_WRITE_CTRL_BATCH_DATA_BUF1_CTRL to store $PC */
+               if (aice_write_ctrl(AICE_WRITE_CTRL_BATCH_DATA_BUF1_CTRL,
+                                       0x40000) != ERROR_OK) {
+                       retval = ERROR_FAIL;
+                       goto end_profiling;
+               }
+
+               aice_usb_run();
+
+               /* enable BATCH command */
+               if (aice_write_ctrl(AICE_WRITE_CTRL_BATCH_CTRL,
+                                       0x80000000) != ERROR_OK) {
+                       aice_usb_halt();
+                       retval = ERROR_FAIL;
+                       goto end_profiling;
+               }
+
+               /* wait a while (AICE bug, workaround) */
+               alive_sleep(this_iteration);
+
+               /* check status */
+               uint32_t i;
+               uint32_t batch_status;
+
+               i = 0;
+               while (1) {
+                       aice_read_ctrl(AICE_READ_CTRL_BATCH_STATUS, &batch_status);
+
+                       if (batch_status & 0x1) {
+                               break;
+                       } else if (batch_status & 0xE) {
+                               aice_usb_halt();
+                               retval = ERROR_FAIL;
+                               goto end_profiling;
+                       }
+
+                       if ((i % 30) == 0)
+                               keep_alive();
+
+                       i++;
+               }
+
+               aice_usb_halt();
+
+               /* get samples from batch data buffer */
+               if (aice_batch_buffer_read(AICE_BATCH_DATA_BUFFER_1,
+                                       samples + iteration_count, this_iteration) != ERROR_OK) {
+                       retval = ERROR_FAIL;
+                       goto end_profiling;
+               }
+
+               iteration_count += this_iteration;
+       }
+
+end_profiling:
+       *num_samples = iteration_count;
+
+       return retval;
+}
+
 /** */
 struct aice_port_api_s aice_usb_api = {
        /** */
@@ -3858,4 +4020,6 @@ struct aice_port_api_s aice_usb_api = {
        .set_count_to_check_dbger = aice_usb_set_count_to_check_dbger,
        /** */
        .set_data_endian = aice_usb_set_data_endian,
+       /** */
+       .profiling = aice_usb_profiling,
 };
index 11bb01d09712411e1607f036b4f1074462345496..49fde0c9abb2c7b89172255fc0d0682b39a4f5d6 100644 (file)
@@ -2455,6 +2455,25 @@ int nds32_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, b
        return ERROR_OK;
 }
 
+int nds32_profiling(struct target *target, uint32_t *samples,
+                       uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
+{
+       /* sample $PC every 10 milliseconds */
+       uint32_t iteration = seconds * 100;
+       struct aice_port_s *aice = target_to_aice(target);
+       struct nds32 *nds32 = target_to_nds32(target);
+
+       if (max_num_samples < iteration)
+               iteration = max_num_samples;
+
+       int pc_regnum = nds32->register_map(nds32, PC);
+       aice->port->api->profiling(10, iteration, pc_regnum, samples, num_samples);
+
+       register_cache_invalidate(nds32->core_cache);
+
+       return ERROR_OK;
+}
+
 int nds32_gdb_fileio_write_memory(struct nds32 *nds32, uint32_t address,
                uint32_t size, const uint8_t *buffer)
 {
index 8acd915f0c1899b5d0d5d7c04305859f84187d5f..304fc35f002211e68aae52d020fccb70194cd3ee 100644 (file)
@@ -422,6 +422,8 @@ extern int nds32_gdb_fileio_write_memory(struct nds32 *nds32, uint32_t address,
 extern int nds32_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
 extern int nds32_reset_halt(struct nds32 *nds32);
 extern int nds32_login(struct nds32 *nds32);
+extern int nds32_profiling(struct target *target, uint32_t *samples,
+                       uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds);
 
 /** Convert target handle to generic Andes target state handle. */
 static inline struct nds32 *target_to_nds32(struct target *target)
index 766e5ccbb8873002f08710c48f019fe20081b572..5996a908095efb2dac80fb08d2abb8b94f2a521e 100644 (file)
@@ -523,4 +523,6 @@ struct target_type nds32_v3_target = {
 
        .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
        .gdb_fileio_end = nds32_gdb_fileio_end,
+
+       .profiling = nds32_profiling,
 };