PID Integrator Hold

Applications, development tools, FPGA, C, WEB
ClaireE47
Posts: 20
Joined: Thu Oct 02, 2014 8:26 am

PID Integrator Hold

Post by ClaireE47 » Sat Oct 25, 2014 1:38 am

Hi,

I've been trying to add an integrator sample-and-hold to the PID controller as it is relevant to the work we are doing but I've had a slight issue. I've changed the controller and FPGA code and uploaded the new image to the board and everything seems to work as it should when I apply a DC signal.
However, when I try to work on our actual signals, the PID doesn't work (just won't do anything). If I reload the original image, then the PID works fine. I've posted the only alteration I made in the Verilog PID Block code (added an if statement for the two ways of triggering the hold) and was hoping someone could have a look to tell me where I might have gone wrong?

Thanks very much

Code: Select all

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      ki_mult  <= {29{1'b0}};
      int_reg  <= {32{1'b0}};
   end
   else begin

      if (int_rst_i) begin
         int_reg <= 32'h0; // reset
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else if ((int_hold_i)||(pid_hold_pin)) begin
         int_reg <= int_reg[32-1:0]; //use sum as is
         ki_mult <= {29{1'b0}};
      end 
      else if (int_sum[33-1:33-2] == 2'b01) begin // positive saturation 
         int_reg <= 32'h7FFFFFFF; // max positive
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else if (int_sum[33-1:33-2] == 2'b10)  begin // negative saturation
         int_reg <= 32'h80000000; // max negative
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else begin
         int_reg <= int_sum[32-1:0]; // use sum as it is
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      end
end

assign int_sum = $signed(ki_mult) + $signed(int_reg) ;
assign int_shr = int_reg[32-1:ISR] ;

Nils Roos
Posts: 1441
Joined: Sat Jun 07, 2014 12:49 pm
Location: Königswinter

Re: PID Integrator Hold

Post by Nils Roos » Sat Oct 25, 2014 4:54 pm

The code snippet looks ok, but of course without seeing the whole picture there's no telling what might go wrong with the hold-signals elsewhere.
If you can share the whole (verilog-) code, more detailed analysis would be possible.

As a general note, the Xilinx tools perform extensive optimisation, and some small error somewhere in your code can often lead to whole sections of logic being discarded, because an input signal was deduced to be constant.

ClaireE47
Posts: 20
Joined: Thu Oct 02, 2014 8:26 am

Re: PID Integrator Hold

Post by ClaireE47 » Sat Oct 25, 2014 9:41 pm

Hey,

These are the three verilog files I altered - in _top.v it was just to add two external triggers, _block.v was what I attached before and in the main pid.v, I've added the inputs and the memory addresses of the software trigger (I put it at an offset of 0x50).

Thanks again

red_pitaya_top.v

Code: Select all

/**
 * $Id: red_pitaya_top.v 1271 2014-02-25 12:32:34Z matej.oblak $
 *
 * @brief Red Pitaya TOP module. It connects external pins and PS part with 
 *        other application modules. 
 *
 * @Author Matej Oblak
 *
 * (c) Red Pitaya  http://www.redpitaya.com
 *
 * This part of code is written in Verilog hardware description language (HDL).
 * Please visit http://en.wikipedia.org/wiki/Verilog
 * for more details on the language used herein.
 */



/**
 * GENERAL DESCRIPTION:
 *
 * Top module connects PS part with rest of Red Pitaya applications.  
 *
 *
 *
 *                   /-------\      
 *   PS DDR <------> |  PS   |      AXI <-> custom bus
 *   PS MIO <------> |   /   | <------------+
 *   PS CLK -------> |  ARM  |              |
 *                   \-------/              |
 *                                          |
 *                                          |
 *                                          |
 *                            /-------\     |
 *                         -> | SCOPE | <---+
 *                         |  \-------/     |
 *                         |                |
 *                         |                |
 *            /--------\   |   /-----\      |
 *   ADC ---> |        | --+-> |     |      |
 *            | ANALOG |       | PID | <----+
 *   DAC <--- |        | <---- |     |      |
 *            \--------/   ^   \-----/      |
 *                         |                |
 *                         |                |
 *                         |  /-------\     |
 *                         -- |  ASG  | <---+ 
 *                            \-------/     |
 *                                          |
 *                                          |
 *                                          |
 *             /--------\                   |
 *    RX ----> |        |                   |
 *   SATA      | DAISY  | <-----------------+
 *    TX <---- |        | 
 *             \--------/ 
 *               |    |
 *               |    |
 *               (FREE)
 *
 *
 *
 *
 * Inside analog module, ADC data is translated from unsigned neg-slope into
 * two's complement. Similar is done on DAC data.
 *
 * Scope module stores data from ADC into RAM, arbitrary signal generator (ASG)
 * sends data from RAM to DAC. MIMO PID uses ADC ADC as input and DAC as its output.
 *
 * Daisy chain connects with other boards with fast serial link. Data which is
 * send and received is at the moment undefined. This is left for the user.
 * 
 */


module red_pitaya_top
(
   // PS connections
 `ifdef TOOL_AHEAD
   inout  [54-1: 0] processing_system7_0_MIO,
   input            processing_system7_0_PS_SRSTB_pin,
   input            processing_system7_0_PS_CLK_pin,
   input            processing_system7_0_PS_PORB_pin,
   inout            processing_system7_0_DDR_Clk,
   inout            processing_system7_0_DDR_Clk_n,
   inout            processing_system7_0_DDR_CKE,
   inout            processing_system7_0_DDR_CS_n,
   inout            processing_system7_0_DDR_RAS_n,
   inout            processing_system7_0_DDR_CAS_n,
   output           processing_system7_0_DDR_WEB_pin,
   inout  [ 3-1: 0] processing_system7_0_DDR_BankAddr,
   inout  [15-1: 0] processing_system7_0_DDR_Addr,
   inout            processing_system7_0_DDR_ODT,
   inout            processing_system7_0_DDR_DRSTB,
   inout  [32-1: 0] processing_system7_0_DDR_DQ,
   inout  [ 4-1: 0] processing_system7_0_DDR_DM,
   inout  [ 4-1: 0] processing_system7_0_DDR_DQS,
   inout  [ 4-1: 0] processing_system7_0_DDR_DQS_n,
   inout            processing_system7_0_DDR_VRN,
   inout            processing_system7_0_DDR_VRP,
 `endif
 `ifdef TOOL_VIVADO
   inout  [54-1: 0] FIXED_IO_mio       ,
   inout            FIXED_IO_ps_clk    ,
   inout            FIXED_IO_ps_porb   ,
   inout            FIXED_IO_ps_srstb  ,
   inout            FIXED_IO_ddr_vrn   ,
   inout            FIXED_IO_ddr_vrp   ,
   inout  [15-1: 0] DDR_addr           ,
   inout  [ 3-1: 0] DDR_ba             ,
   inout            DDR_cas_n          ,
   inout            DDR_ck_n           ,
   inout            DDR_ck_p           ,
   inout            DDR_cke            ,
   inout            DDR_cs_n           ,
   inout  [ 4-1: 0] DDR_dm             ,
   inout  [32-1: 0] DDR_dq             ,
   inout  [ 4-1: 0] DDR_dqs_n          ,
   inout  [ 4-1: 0] DDR_dqs_p          ,
   inout            DDR_odt            ,
   inout            DDR_ras_n          ,
   inout            DDR_reset_n        ,
   inout            DDR_we_n           ,
 `endif

   // Red Pitaya periphery
  
   // ADC
   input  [16-1: 2] adc_dat_a_i        ,  // ADC CH1
   input  [16-1: 2] adc_dat_b_i        ,  // ADC CH2
   input            adc_clk_p_i        ,  // ADC data clock
   input            adc_clk_n_i        ,  // ADC data clock
   output [ 2-1: 0] adc_clk_o          ,  // optional ADC clock source
   output           adc_cdcs_o         ,  // ADC clock duty cycle stabilizer
  
   // DAC
   output [14-1: 0] dac_dat_o          ,  // DAC combined data
   output           dac_wrt_o          ,  // DAC write
   output           dac_sel_o          ,  // DAC channel select
   output           dac_clk_o          ,  // DAC clock
   output           dac_rst_o          ,  // DAC reset
  
   // PWM DAC
   output [ 4-1: 0] dac_pwm_o          ,  // serial PWM DAC

   // XADC
   input  [ 5-1: 0] vinp_i             ,  // voltages p
   input  [ 5-1: 0] vinn_i             ,  // voltages n

   // Expansion connector
   inout  [ 8-1: 0] exp_p_io           ,
   inout  [ 8-1: 0] exp_n_io           ,


   // SATA connector
   output [ 2-1: 0] daisy_p_o          ,  // line 1 is clock capable
   output [ 2-1: 0] daisy_n_o          ,
   input  [ 2-1: 0] daisy_p_i          ,  // line 1 is clock capable
   input  [ 2-1: 0] daisy_n_i          ,

   // LED
   output [ 8-1: 0] led_o       

);






//---------------------------------------------------------------------------------
//
//  Connections to PS

wire  [  4-1: 0] fclk               ; //[0]-125MHz, [1]-250MHz, [2]-50MHz, [3]-200MHz
wire  [  4-1: 0] frstn              ;

wire             ps_sys_clk         ;
wire             ps_sys_rstn        ;
wire  [ 32-1: 0] ps_sys_addr        ;
wire  [ 32-1: 0] ps_sys_wdata       ;
wire  [  4-1: 0] ps_sys_sel         ;
wire             ps_sys_wen         ;
wire             ps_sys_ren         ;
wire  [ 32-1: 0] ps_sys_rdata       ;
wire             ps_sys_err         ;
wire             ps_sys_ack         ;

  
red_pitaya_ps i_ps
(
 `ifdef TOOL_AHEAD
  .processing_system7_0_MIO          (  processing_system7_0_MIO            ),
  .processing_system7_0_PS_SRSTB_pin (  processing_system7_0_PS_SRSTB_pin   ),
  .processing_system7_0_PS_CLK_pin   (  processing_system7_0_PS_CLK_pin     ),
  .processing_system7_0_PS_PORB_pin  (  processing_system7_0_PS_PORB_pin    ),
  .processing_system7_0_DDR_Clk      (  processing_system7_0_DDR_Clk        ),
  .processing_system7_0_DDR_Clk_n    (  processing_system7_0_DDR_Clk_n      ),
  .processing_system7_0_DDR_CKE      (  processing_system7_0_DDR_CKE        ),
  .processing_system7_0_DDR_CS_n     (  processing_system7_0_DDR_CS_n       ),
  .processing_system7_0_DDR_RAS_n    (  processing_system7_0_DDR_RAS_n      ),
  .processing_system7_0_DDR_CAS_n    (  processing_system7_0_DDR_CAS_n      ),
  .processing_system7_0_DDR_WEB_pin  (  processing_system7_0_DDR_WEB_pin    ),
  .processing_system7_0_DDR_BankAddr (  processing_system7_0_DDR_BankAddr   ),
  .processing_system7_0_DDR_Addr     (  processing_system7_0_DDR_Addr       ),
  .processing_system7_0_DDR_ODT      (  processing_system7_0_DDR_ODT        ),
  .processing_system7_0_DDR_DRSTB    (  processing_system7_0_DDR_DRSTB      ),
  .processing_system7_0_DDR_DQ       (  processing_system7_0_DDR_DQ         ),
  .processing_system7_0_DDR_DM       (  processing_system7_0_DDR_DM         ),
  .processing_system7_0_DDR_DQS      (  processing_system7_0_DDR_DQS        ),
  .processing_system7_0_DDR_DQS_n    (  processing_system7_0_DDR_DQS_n      ),
  .processing_system7_0_DDR_VRN      (  processing_system7_0_DDR_VRN        ),
  .processing_system7_0_DDR_VRP      (  processing_system7_0_DDR_VRP        ),
 `endif
 `ifdef TOOL_VIVADO
  .FIXED_IO_mio       (  FIXED_IO_mio                ),
  .FIXED_IO_ps_clk    (  FIXED_IO_ps_clk             ),
  .FIXED_IO_ps_porb   (  FIXED_IO_ps_porb            ),
  .FIXED_IO_ps_srstb  (  FIXED_IO_ps_srstb           ),
  .FIXED_IO_ddr_vrn   (  FIXED_IO_ddr_vrn            ),
  .FIXED_IO_ddr_vrp   (  FIXED_IO_ddr_vrp            ),
  .DDR_addr           (  DDR_addr                    ),
  .DDR_ba             (  DDR_ba                      ),
  .DDR_cas_n          (  DDR_cas_n                   ),
  .DDR_ck_n           (  DDR_ck_n                    ),
  .DDR_ck_p           (  DDR_ck_p                    ),
  .DDR_cke            (  DDR_cke                     ),
  .DDR_cs_n           (  DDR_cs_n                    ),
  .DDR_dm             (  DDR_dm                      ),
  .DDR_dq             (  DDR_dq                      ),
  .DDR_dqs_n          (  DDR_dqs_n                   ),
  .DDR_dqs_p          (  DDR_dqs_p                   ),
  .DDR_odt            (  DDR_odt                     ),
  .DDR_ras_n          (  DDR_ras_n                   ),
  .DDR_reset_n        (  DDR_reset_n                 ),
  .DDR_we_n           (  DDR_we_n                    ),
 `endif

  .fclk_clk_o      (  fclk               ),
  .fclk_rstn_o     (  frstn              ),

   // system read/write channel
  .sys_clk_o       (  ps_sys_clk         ),  // system clock
  .sys_rstn_o      (  ps_sys_rstn        ),  // system reset - active low
  .sys_addr_o      (  ps_sys_addr        ),  // system read/write address
  .sys_wdata_o     (  ps_sys_wdata       ),  // system write data
  .sys_sel_o       (  ps_sys_sel         ),  // system write byte select
  .sys_wen_o       (  ps_sys_wen         ),  // system write enable
  .sys_ren_o       (  ps_sys_ren         ),  // system read enable
  .sys_rdata_i     (  ps_sys_rdata       ),  // system read data
  .sys_err_i       (  ps_sys_err         ),  // system error indicator
  .sys_ack_i       (  ps_sys_ack         ),  // system acknowledge signal

   // SPI master
  .spi_ss_o        (                     ),  // select slave 0
  .spi_ss1_o       (                     ),  // select slave 1
  .spi_ss2_o       (                     ),  // select slave 2
  .spi_sclk_o      (                     ),  // serial clock
  .spi_mosi_o      (                     ),  // master out slave in
  .spi_miso_i      (  1'b0               ),  // master in slave out

   // SPI slave
  .spi_ss_i        (  1'b1               ),  // slave selected
  .spi_sclk_i      (  1'b0               ),  // serial clock
  .spi_mosi_i      (  1'b0               ),  // master out slave in
  .spi_miso_o      (                     )   // master in slave out

);








//---------------------------------------------------------------------------------
//
//  Analog peripherials 

// ADC clock duty cycle stabilizer is enabled
assign adc_cdcs_o = 1'b1 ;

// generating ADC clock is disabled
assign adc_clk_o = 2'b10;
//ODDR i_adc_clk_p ( .Q(adc_clk_o[0]), .D1(1'b1), .D2(1'b0), .C(fclk[0]), .CE(1'b1), .R(1'b0), .S(1'b0));
//ODDR i_adc_clk_n ( .Q(adc_clk_o[1]), .D1(1'b0), .D2(1'b1), .C(fclk[0]), .CE(1'b1), .R(1'b0), .S(1'b0));


wire             ser_clk     ;
wire             adc_clk     ;
reg              adc_rstn    ;
wire  [ 14-1: 0] adc_a       ;
wire  [ 14-1: 0] adc_b       ;
reg   [ 14-1: 0] dac_a       ;
reg   [ 14-1: 0] dac_b       ;
wire  [ 24-1: 0] dac_pwm_a   ;
wire  [ 24-1: 0] dac_pwm_b   ;
wire  [ 24-1: 0] dac_pwm_c   ;
wire  [ 24-1: 0] dac_pwm_d   ;

red_pitaya_analog i_analog
(
  // ADC IC
  .adc_dat_a_i        (  adc_dat_a_i      ),  // CH 1
  .adc_dat_b_i        (  adc_dat_b_i      ),  // CH 2
  .adc_clk_p_i        (  adc_clk_p_i      ),  // data clock
  .adc_clk_n_i        (  adc_clk_n_i      ),  // data clock
  
  // DAC IC
  .dac_dat_o          (  dac_dat_o        ),  // combined data
  .dac_wrt_o          (  dac_wrt_o        ),  // write enable
  .dac_sel_o          (  dac_sel_o        ),  // channel select
  .dac_clk_o          (  dac_clk_o        ),  // clock
  .dac_rst_o          (  dac_rst_o        ),  // reset
  
  // PWM DAC
  .dac_pwm_o          (  dac_pwm_o        ),  // serial PWM DAC
  
  
  // user interface
  .adc_dat_a_o        (  adc_a            ),  // ADC CH1
  .adc_dat_b_o        (  adc_b            ),  // ADC CH2
  .adc_clk_o          (  adc_clk          ),  // ADC received clock
  .adc_rst_i          (  adc_rstn         ),  // reset - active low
  .ser_clk_o          (  ser_clk          ),  // fast serial clock

  .dac_dat_a_i        (  dac_a            ),  // DAC CH1
  .dac_dat_b_i        (  dac_b            ),  // DAC CH2

  .dac_pwm_a_i        (  dac_pwm_a        ),  // slow DAC CH1
  .dac_pwm_b_i        (  dac_pwm_b        ),  // slow DAC CH2
  .dac_pwm_c_i        (  dac_pwm_c        ),  // slow DAC CH3
  .dac_pwm_d_i        (  dac_pwm_d        ),  // slow DAC CH4
  .dac_pwm_sync_o     (                   )   // slow DAC sync
);

always @(posedge adc_clk) begin
   adc_rstn <= frstn[0] ;
end








//---------------------------------------------------------------------------------
//
//  system bus decoder & multiplexer
//  it breaks memory addresses into 8 regions

wire                sys_clk    = ps_sys_clk      ;
wire                sys_rstn   = ps_sys_rstn     ;
wire  [    32-1: 0] sys_addr   = ps_sys_addr     ;
wire  [    32-1: 0] sys_wdata  = ps_sys_wdata    ;
wire  [     4-1: 0] sys_sel    = ps_sys_sel      ;
wire  [     8-1: 0] sys_wen    ;
wire  [     8-1: 0] sys_ren    ;
wire  [(8*32)-1: 0] sys_rdata  ;
wire  [ (8*1)-1: 0] sys_err    ;
wire  [ (8*1)-1: 0] sys_ack    ;
reg   [     8-1: 0] sys_cs     ;

always @(sys_addr) begin
   sys_cs = 8'h0 ;
   case (sys_addr[22:20])
      3'h0, 3'h1, 3'h2, 3'h3, 3'h4, 3'h5, 3'h6, 3'h7 : 
         sys_cs[sys_addr[22:20]] = 1'b1 ; 
   endcase
end

assign sys_wen = sys_cs & {8{ps_sys_wen}}  ;
assign sys_ren = sys_cs & {8{ps_sys_ren}}  ;


assign ps_sys_rdata = {32{sys_cs[ 0]}} & sys_rdata[ 0*32+31: 0*32] |
                      {32{sys_cs[ 1]}} & sys_rdata[ 1*32+31: 1*32] |
                      {32{sys_cs[ 2]}} & sys_rdata[ 2*32+31: 2*32] |
                      {32{sys_cs[ 3]}} & sys_rdata[ 3*32+31: 3*32] |
                      {32{sys_cs[ 4]}} & sys_rdata[ 4*32+31: 4*32] | 
                      {32{sys_cs[ 5]}} & sys_rdata[ 5*32+31: 5*32] |
                      {32{sys_cs[ 6]}} & sys_rdata[ 6*32+31: 6*32] |
                      {32{sys_cs[ 7]}} & sys_rdata[ 7*32+31: 7*32] ; 

assign ps_sys_err   = sys_cs[ 0] & sys_err[  0] |
                      sys_cs[ 1] & sys_err[  1] |
                      sys_cs[ 2] & sys_err[  2] |
                      sys_cs[ 3] & sys_err[  3] |
                      sys_cs[ 4] & sys_err[  4] | 
                      sys_cs[ 5] & sys_err[  5] |
                      sys_cs[ 6] & sys_err[  6] |
                      sys_cs[ 7] & sys_err[  7] ; 

assign ps_sys_ack   = sys_cs[ 0] & sys_ack[  0] |
                      sys_cs[ 1] & sys_ack[  1] |
                      sys_cs[ 2] & sys_ack[  2] |
                      sys_cs[ 3] & sys_ack[  3] |
                      sys_cs[ 4] & sys_ack[  4] | 
                      sys_cs[ 5] & sys_ack[  5] |
                      sys_cs[ 6] & sys_ack[  6] |
                      sys_cs[ 7] & sys_ack[  7] ; 


assign sys_rdata[ 6*32+31: 6*32] = 32'h0;

assign sys_err[6] = {1{1'b0}} ;
assign sys_ack[6] = {1{1'b1}} ;








//---------------------------------------------------------------------------------
//
//  House Keeping

wire  [  8-1: 0] exp_p_in     ;
wire  [  8-1: 0] exp_p_out    ;
wire  [  8-1: 0] exp_p_dir    ;
wire  [  8-1: 0] exp_n_in     ;
wire  [  8-1: 0] exp_n_out    ;
wire  [  8-1: 0] exp_n_dir    ;

red_pitaya_hk i_hk
(
  .clk_i           (  adc_clk                    ),  // clock
  .rstn_i          (  adc_rstn                   ),  // reset - active low

  // LED
  .led_o           (  led_o                      ),  // LED output
   // Expansion connector
  .exp_p_dat_i     (  exp_p_in                   ),  // input data
  .exp_p_dat_o     (  exp_p_out                  ),  // output data
  .exp_p_dir_o     (  exp_p_dir                  ),  // 1-output enable
  .exp_n_dat_i     (  exp_n_in                   ),
  .exp_n_dat_o     (  exp_n_out                  ),
  .exp_n_dir_o     (  exp_n_dir                  ),

   // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[0]                 ),  // write enable
  .sys_ren_i       (  sys_ren[0]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 0*32+31: 0*32]  ),  // read data
  .sys_err_o       (  sys_err[0]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[0]                 )   // acknowledge signal
);



genvar GV ;

generate
for( GV = 0 ; GV < 8 ; GV = GV + 1)
begin : exp_iobuf
  IOBUF i_iobufp (.O(exp_p_in[GV]), .IO(exp_p_io[GV]), .I(exp_p_out[GV]), .T(!exp_p_dir[GV]) );
  IOBUF i_iobufn (.O(exp_n_in[GV]), .IO(exp_n_io[GV]), .I(exp_n_out[GV]), .T(!exp_n_dir[GV]) );
end
endgenerate





//---------------------------------------------------------------------------------
//
//  Oscilloscope application

wire trig_asg_out ;

red_pitaya_scope i_scope
(
  // ADC
  .adc_a_i         (  adc_a                      ),  // CH 1
  .adc_b_i         (  adc_b                      ),  // CH 2
  .adc_clk_i       (  adc_clk                    ),  // clock
  .adc_rstn_i      (  adc_rstn                   ),  // reset - active low
  .trig_ext_i      (  exp_p_in[0]                ),  // external trigger
  .trig_asg_i      (  trig_asg_out               ),  // ASG trigger

   // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[1]                 ),  // write enable
  .sys_ren_i       (  sys_ren[1]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 1*32+31: 1*32]  ),  // read data
  .sys_err_o       (  sys_err[1]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[1]                 )   // acknowledge signal
);








//---------------------------------------------------------------------------------
//
//  DAC arbitrary signal generator

wire  [ 14-1: 0] asg_a       ;
wire  [ 14-1: 0] asg_b       ;

red_pitaya_asg i_asg
(
   // DAC
  .dac_a_o         (  asg_a                      ),  // CH 1
  .dac_b_o         (  asg_b                      ),  // CH 2
  .dac_clk_i       (  adc_clk                    ),  // clock
  .dac_rstn_i      (  adc_rstn                   ),  // reset - active low
  .trig_a_i        (  exp_p_in[0]                ),
  .trig_b_i        (  exp_p_in[0]                ),
  .trig_out_o      (  trig_asg_out               ),

  // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[2]                 ),  // write enable
  .sys_ren_i       (  sys_ren[2]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 2*32+31: 2*32]  ),  // read data
  .sys_err_o       (  sys_err[2]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[2]                 )   // acknowledge signal
);








//---------------------------------------------------------------------------------
//
//  MIMO PID controller

wire  [ 14-1: 0] pid_a       ;
wire  [ 14-1: 0] pid_b       ;

red_pitaya_pid i_pid
(
   // signals
  .clk_i           (  adc_clk                    ),  // clock
  .rstn_i          (  adc_rstn                   ),  // reset - active low
  .dat_a_i         (  adc_a                      ),  // in 1
  .dat_b_i         (  adc_b                      ),  // in 2
  .dat_a_o         (  pid_a                      ),  // out 1
  .dat_b_o         (  pid_b                      ),  // out 2
  
  .hold_input1	   (  exp_p_in[0]		 ),  //hold input 1
  .hold_input2 	   (  exp_p_in[1]		 ),  //hold input 2

  // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[3]                 ),  // write enable
  .sys_ren_i       (  sys_ren[3]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 3*32+31: 3*32]  ),  // read data
  .sys_err_o       (  sys_err[3]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[3]                 )   // acknowledge signal
);




//---------------------------------------------------------------------------------
//
//  Sumation of ASG and PID signal
//  perform saturation before sending to DAC 

wire  [ 15-1: 0] dac_a_sum       ;
wire  [ 15-1: 0] dac_b_sum       ;

assign dac_a_sum = $signed(asg_a) + $signed(pid_a);
assign dac_b_sum = $signed(asg_b) + $signed(pid_b);

always @(*) begin
   if (dac_a_sum[15-1:15-2] == 2'b01) // pos. overflow
      dac_a <= 14'h1FFF ;
   else if (dac_a_sum[15-1:15-2] == 2'b10) // neg. overflow
      dac_a <= 14'h2000 ;
   else
      dac_a <= dac_a_sum[14-1:0] ;


   if (dac_b_sum[15-1:15-2] == 2'b01) // pos. overflow
      dac_b <= 14'h1FFF ;
   else if (dac_b_sum[15-1:15-2] == 2'b10) // neg. overflow
      dac_b <= 14'h2000 ;
   else
      dac_b <= dac_b_sum[14-1:0] ;
end










//---------------------------------------------------------------------------------
//
//  Analog mixed signals
//  XADC and slow PWM DAC control

red_pitaya_ams i_ams
(
   // power test
  .clk_i           (  adc_clk                    ),  // clock
  .rstn_i          (  adc_rstn                   ),  // reset - active low

  .vinp_i          (  vinp_i                     ),  // voltages p
  .vinn_i          (  vinn_i                     ),  // voltages n

  .dac_a_o         (  dac_pwm_a                  ),  // values used for
  .dac_b_o         (  dac_pwm_b                  ),  // conversion into PWM signal
  .dac_c_o         (  dac_pwm_c                  ),
  .dac_d_o         (  dac_pwm_d                  ),

   // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[4]                 ),  // write enable
  .sys_ren_i       (  sys_ren[4]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 4*32+31: 4*32]  ),  // read data
  .sys_err_o       (  sys_err[4]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[4]                 )   // acknowledge signal
);








//---------------------------------------------------------------------------------
//
//  Daisy chain
//  simple communication module

wire daisy_rx_rdy ;
wire dly_clk = fclk[3]; // 200MHz clock from PS - used for IDELAY (optionaly)

red_pitaya_daisy i_daisy
(
   // SATA connector
  .daisy_p_o       (  daisy_p_o                  ),  // line 1 is clock capable
  .daisy_n_o       (  daisy_n_o                  ),
  .daisy_p_i       (  daisy_p_i                  ),  // line 1 is clock capable
  .daisy_n_i       (  daisy_n_i                  ),

   // Data
  .ser_clk_i       (  ser_clk                    ),  // high speed serial
  .dly_clk_i       (  dly_clk                    ),  // delay clock
   // TX
  .par_clk_i       (  adc_clk                    ),  // data paralel clock
  .par_rstn_i      (  adc_rstn                   ),  // reset - active low
  .par_rdy_o       (  daisy_rx_rdy               ),
  .par_dv_i        (  daisy_rx_rdy               ),
  .par_dat_i       (  16'h1234                   ),
   // RX
  .par_clk_o       (                             ),
  .par_rstn_o      (                             ),
  .par_dv_o        (                             ),
  .par_dat_o       (                             ),

  .debug_o         (/*led_o*/                    ),

   // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[5]                 ),  // write enable
  .sys_ren_i       (  sys_ren[5]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 5*32+31: 5*32]  ),  // read data
  .sys_err_o       (  sys_err[5]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[5]                 )   // acknowledge signal
);









//---------------------------------------------------------------------------------
//
//  Power consumtion test

red_pitaya_test i_test
(
   // power test
  .clk_i           (  adc_clk                    ),  // clock
  .rstn_i          (  adc_rstn                   ),  // reset - active low

  .rand_o          (                             ),

   // System bus
  .sys_clk_i       (  sys_clk                    ),  // clock
  .sys_rstn_i      (  sys_rstn                   ),  // reset - active low
  .sys_addr_i      (  sys_addr                   ),  // address
  .sys_wdata_i     (  sys_wdata                  ),  // write data
  .sys_sel_i       (  sys_sel                    ),  // write byte select
  .sys_wen_i       (  sys_wen[7]                 ),  // write enable
  .sys_ren_i       (  sys_ren[7]                 ),  // read enable
  .sys_rdata_o     (  sys_rdata[ 7*32+31: 7*32]  ),  // read data
  .sys_err_o       (  sys_err[7]                 ),  // error indicator
  .sys_ack_o       (  sys_ack[7]                 )   // acknowledge signal
);

//assign sys_rdata[ 7*32+31: 7*32] = 32'h0 ; 
//assign sys_err[7] = 1'b0 ;
//assign sys_ack[7] = 1'b1 ;




















endmodule

red_pitaya_pid.v

Code: Select all

/**
 * $Id: red_pitaya_pid.v 961 2014-01-21 11:40:39Z matej.oblak $
 *
 * @brief Red Pitaya MIMO PID controller.
 *
 * @Author Matej Oblak
 *
 * (c) Red Pitaya  http://www.redpitaya.com
 *
 * This part of code is written in Verilog hardware description language (HDL).
 * Please visit http://en.wikipedia.org/wiki/Verilog
 * for more details on the language used herein.
 */



/**
 * GENERAL DESCRIPTION:
 *
 * Multiple input multiple output controller.
 *
 *
 *                 /-------\       /-----------\
 *   CHA -----+--> | PID11 | ------| SUM & SAT | ---> CHA
 *            |    \-------/       \-----------/
 *            |                            ^
 *            |    /-------\               |
 *            ---> | PID21 | ----------    |
 *                 \-------/           |   |
 *                                     |   |
 *  INPUT                              |   |         OUTPUT
 *                                     |   |
 *                 /-------\           |   |
 *            ---> | PID12 | --------------
 *            |    \-------/           |    
 *            |                        Àá
 *            |    /-------\       /-----------\
 *   CHB -----+--> | PID22 | ------| SUM & SAT | ---> CHB
 *                 \-------/       \-----------/
 *
 *
 * MIMO controller is build from four equal submodules, each can have 
 * different settings.
 *
 * Each output is sum of two controllers with different input. That sum is also
 * saturated to protect from wrapping.
 * 
 */



module red_pitaya_pid
(
   // signals
   input                 clk_i           ,  //!< processing clock
   input                 rstn_i          ,  //!< processing reset - active low
   input      [ 14-1: 0] dat_a_i         ,  //!< input data CHA
   input      [ 14-1: 0] dat_b_i         ,  //!< input data CHB
   output     [ 14-1: 0] dat_a_o         ,  //!< output data CHA
   output     [ 14-1: 0] dat_b_o         ,  //!< output data CHB

   input      		 hold_input1     ,  //hold input 1
   input      		 hold_input2     ,  //hold input 2
  
   // system bus
   input                 sys_clk_i       ,  //!< bus clock
   input                 sys_rstn_i      ,  //!< bus reset - active low
   input      [ 32-1: 0] sys_addr_i      ,  //!< bus address
   input      [ 32-1: 0] sys_wdata_i     ,  //!< bus write data
   input      [  4-1: 0] sys_sel_i       ,  //!< bus write byte select
   input                 sys_wen_i       ,  //!< bus write enable
   input                 sys_ren_i       ,  //!< bus read enable
   output     [ 32-1: 0] sys_rdata_o     ,  //!< bus read data
   output                sys_err_o       ,  //!< bus error indicator
   output                sys_ack_o          //!< bus acknowledge signal
);




wire [ 32-1: 0] addr         ;
wire [ 32-1: 0] wdata        ;
wire            wen          ;
wire            ren          ;
reg  [ 32-1: 0] rdata        ;
reg             err          ;
reg             ack          ;


localparam  PSR = 12         ;
localparam  ISR = 18         ;
localparam  DSR = 10         ;


//---------------------------------------------------------------------------------
//  PID 11

wire [ 14-1: 0] pid_11_out   ;
reg  [ 14-1: 0] set_11_sp    ;
reg  [ 14-1: 0] set_11_kp    ;
reg  [ 14-1: 0] set_11_ki    ;
reg  [ 14-1: 0] set_11_kd    ;
reg             set_11_irst  ;
reg             set_11_ihold ;

red_pitaya_pid_block #(
  .PSR (  PSR   ),
  .ISR (  ISR   ),
  .DSR (  DSR   )      
)
i_pid11
(
   // data
  .clk_i        (  clk_i          ),  // clock
  .rstn_i       (  rstn_i         ),  // reset - active low
  .dat_i        (  dat_a_i        ),  // input data
  .dat_o        (  pid_11_out     ),  // output data

  .pid_hold_pin (  hold_input1    ), //external

   // settings
  .set_sp_i     (  set_11_sp      ),  // set point
  .set_kp_i     (  set_11_kp      ),  // Kp
  .set_ki_i     (  set_11_ki      ),  // Ki
  .set_kd_i     (  set_11_kd      ),  // Kd
  .int_rst_i    (  set_11_irst    ),   // integrator reset

  .int_hold_i   (  set_11_ihold   ) //internal
);




//---------------------------------------------------------------------------------
//  PID 21

wire [ 14-1: 0] pid_21_out   ;
reg  [ 14-1: 0] set_21_sp    ;
reg  [ 14-1: 0] set_21_kp    ;
reg  [ 14-1: 0] set_21_ki    ;
reg  [ 14-1: 0] set_21_kd    ;
reg             set_21_irst  ;
reg             set_21_ihold ;


red_pitaya_pid_block #(
  .PSR (  PSR   ),
  .ISR (  ISR   ),
  .DSR (  DSR   )      
)
i_pid21
(
   // data
  .clk_i        (  clk_i          ),  // clock
  .rstn_i       (  rstn_i         ),  // reset - active low
  .dat_i        (  dat_a_i        ),  // input data
  .dat_o        (  pid_21_out     ),  // output data

  .pid_hold_pin (  hold_input1    ), //external

   // settings
  .set_sp_i     (  set_21_sp      ),  // set point
  .set_kp_i     (  set_21_kp      ),  // Kp
  .set_ki_i     (  set_21_ki      ),  // Ki
  .set_kd_i     (  set_21_kd      ),  // Kd
  .int_rst_i    (  set_21_irst    ),   // integrator reset

  .int_hold_i   (  set_21_ihold   ) //internal
);




//---------------------------------------------------------------------------------
//  PID 12

wire [ 14-1: 0] pid_12_out   ;
reg  [ 14-1: 0] set_12_sp    ;
reg  [ 14-1: 0] set_12_kp    ;
reg  [ 14-1: 0] set_12_ki    ;
reg  [ 14-1: 0] set_12_kd    ;
reg             set_12_irst  ;
reg             set_12_ihold ;


red_pitaya_pid_block #(
  .PSR (  PSR   ),
  .ISR (  ISR   ),
  .DSR (  DSR   )      
)
i_pid12
(
   // data
  .clk_i        (  clk_i          ),  // clock
  .rstn_i       (  rstn_i         ),  // reset - active low
  .dat_i        (  dat_b_i        ),  // input data
  .dat_o        (  pid_12_out     ),  // output data

  .pid_hold_pin (  hold_input2    ), //external

   // settings
  .set_sp_i     (  set_12_sp      ),  // set point
  .set_kp_i     (  set_12_kp      ),  // Kp
  .set_ki_i     (  set_12_ki      ),  // Ki
  .set_kd_i     (  set_12_kd      ),  // Kd
  .int_rst_i    (  set_12_irst    ),   // integrator reset

  .int_hold_i   (  set_12_ihold   ) //internal
);




//---------------------------------------------------------------------------------
//  PID 22

wire [ 14-1: 0] pid_22_out   ;
reg  [ 14-1: 0] set_22_sp    ;
reg  [ 14-1: 0] set_22_kp    ;
reg  [ 14-1: 0] set_22_ki    ;
reg  [ 14-1: 0] set_22_kd    ;
reg             set_22_irst  ;
reg             set_22_ihold ;

red_pitaya_pid_block #(
  .PSR (  PSR   ),
  .ISR (  ISR   ),
  .DSR (  DSR   )      
)
i_pid22
(
   // data
  .clk_i        (  clk_i          ),  // clock
  .rstn_i       (  rstn_i         ),  // reset - active low
  .dat_i        (  dat_b_i        ),  // input data
  .dat_o        (  pid_22_out     ),  // output data

  .pid_hold_pin (  hold_input2    ), //external

   // settings
  .set_sp_i     (  set_22_sp      ),  // set point
  .set_kp_i     (  set_22_kp      ),  // Kp
  .set_ki_i     (  set_22_ki      ),  // Ki
  .set_kd_i     (  set_22_kd      ),  // Kd
  .int_rst_i    (  set_22_irst    ),   // integrator reset

  .int_hold_i   (  set_22_ihold   ) //internal
);





//---------------------------------------------------------------------------------
//  Sum and saturation

wire [ 15-1: 0] out_1_sum   ;
reg  [ 14-1: 0] out_1_sat   ;
wire [ 15-1: 0] out_2_sum   ;
reg  [ 14-1: 0] out_2_sat   ;

assign out_1_sum = $signed(pid_11_out) + $signed(pid_12_out);
assign out_2_sum = $signed(pid_22_out) + $signed(pid_21_out);

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      out_1_sat <= 14'd0 ;
      out_2_sat <= 14'd0 ;
   end
   else begin
      if (out_1_sum[15-1:15-2]==2'b01) // postitive sat
         out_1_sat <= 14'h1FFF ;
      else if (out_1_sum[15-1:15-2]==2'b10) // negative sat
         out_1_sat <= 14'h2000 ;
      else
         out_1_sat <= out_1_sum[14-1:0] ;

      if (out_2_sum[15-1:15-2]==2'b01) // postitive sat
         out_2_sat <= 14'h1FFF ;
      else if (out_2_sum[15-1:15-2]==2'b10) // negative sat
         out_2_sat <= 14'h2000 ;
      else
         out_2_sat <= out_2_sum[14-1:0] ;
   end
end


assign dat_a_o = out_1_sat ;
assign dat_b_o = out_2_sat ;














//---------------------------------------------------------------------------------
//
//  System bus connection


always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      set_11_sp    <= 14'd0 ;
      set_11_kp    <= 14'd0 ;
      set_11_ki    <= 14'd0 ;
      set_11_kd    <= 14'd0 ;
      set_11_irst  <=  1'b1 ;
      set_11_ihold <=  1'b0 ;
      set_12_sp    <= 14'd0 ;
      set_12_kp    <= 14'd0 ;
      set_12_ki    <= 14'd0 ;
      set_12_kd    <= 14'd0 ;
      set_12_irst  <=  1'b1 ;
      set_12_ihold <=  1'b0 ;
      set_21_sp    <= 14'd0 ;
      set_21_kp    <= 14'd0 ;
      set_21_ki    <= 14'd0 ;
      set_21_kd    <= 14'd0 ;
      set_21_irst  <=  1'b1 ;
      set_21_ihold <=  1'b0 ;
      set_22_sp    <= 14'd0 ;
      set_22_kp    <= 14'd0 ;
      set_22_ki    <= 14'd0 ;
      set_22_kd    <= 14'd0 ;
      set_22_irst  <=  1'b1 ;
      set_22_ihold <=  1'b0 ;

   end
   else begin
      if (wen) begin
         if (addr[19:0]==16'h0)    {set_22_irst,set_21_irst,set_12_irst,set_11_irst} <= wdata[ 4-1:0] ;

         if (addr[19:0]==16'h10)    set_11_sp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h14)    set_11_kp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h18)    set_11_ki  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h1C)    set_11_kd  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h20)    set_12_sp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h24)    set_12_kp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h28)    set_12_ki  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h2C)    set_12_kd  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h30)    set_21_sp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h34)    set_21_kp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h38)    set_21_ki  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h3C)    set_21_kd  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h40)    set_22_sp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h44)    set_22_kp  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h48)    set_22_ki  <= wdata[14-1:0] ;
         if (addr[19:0]==16'h4C)    set_22_kd  <= wdata[14-1:0] ;

         if (addr[19:0]==16'h50)    {set_22_ihold,set_21_ihold,set_12_ihold,set_11_ihold} <= wdata[ 4-1:0] ;

      end
   end
end





always @(*) begin
   err <= 1'b0 ;

   casez (addr[19:0])
      20'h00 : begin ack <= 1'b1;          rdata <= {{32- 4{1'b0}}, set_22_irst,set_21_irst,set_12_irst,set_11_irst}       ; end 

      20'h10 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_11_sp}          ; end 
      20'h14 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_11_kp}          ; end 
      20'h18 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_11_ki}          ; end 
      20'h1C : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_11_kd}          ; end 

      20'h20 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_12_sp}          ; end 
      20'h24 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_12_kp}          ; end 
      20'h28 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_12_ki}          ; end 
      20'h2C : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_12_kd}          ; end 

      20'h30 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_21_sp}          ; end 
      20'h34 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_21_kp}          ; end 
      20'h38 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_21_ki}          ; end 
      20'h3C : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_21_kd}          ; end 

      20'h40 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_22_sp}          ; end 
      20'h44 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_22_kp}          ; end 
      20'h48 : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_22_ki}          ; end 
      20'h4C : begin ack <= 1'b1;          rdata <= {{32-14{1'b0}}, set_22_kd}          ; end 

      20'h50 : begin ack <= 1'b1;          rdata <= {{32- 4{1'b0}}, set_22_ihold,set_21_ihold,set_12_ihold,set_11_ihold}       ; end 

     default : begin ack <= 1'b1;          rdata <=  32'h0                              ; end
   endcase
end






// bridge between processing and sys clock
bus_clk_bridge i_bridge
(
   .sys_clk_i     (  sys_clk_i      ),
   .sys_rstn_i    (  sys_rstn_i     ),
   .sys_addr_i    (  sys_addr_i     ),
   .sys_wdata_i   (  sys_wdata_i    ),
   .sys_sel_i     (  sys_sel_i      ),
   .sys_wen_i     (  sys_wen_i      ),
   .sys_ren_i     (  sys_ren_i      ),
   .sys_rdata_o   (  sys_rdata_o    ),
   .sys_err_o     (  sys_err_o      ),
   .sys_ack_o     (  sys_ack_o      ),

   .clk_i         (  clk_i          ),
   .rstn_i        (  rstn_i         ),
   .addr_o        (  addr           ),
   .wdata_o       (  wdata          ),
   .wen_o         (  wen            ),
   .ren_o         (  ren            ),
   .rdata_i       (  rdata          ),
   .err_i         (  err            ),
   .ack_i         (  ack            )
);






endmodule

red_pitaya_pid_block.v

Code: Select all

/**
 * $Id: red_pitaya_pid_block.v 961 2014-01-21 11:40:39Z matej.oblak $
 *
 * @brief Red Pitaya PID controller.
 *
 * @Author Matej Oblak
 *
 * (c) Red Pitaya  http://www.redpitaya.com
 *
 * This part of code is written in Verilog hardware description language (HDL).
 * Please visit http://en.wikipedia.org/wiki/Verilog
 * for more details on the language used herein.
 */



/**
 * GENERAL DESCRIPTION:
 *
 * Proportional-integral-derivative (PID) controller.
 *
 *
 *        /---\         /---\      /-----------\
 *   IN --| - |----+--> | P | ---> | SUM & SAT | ---> OUT
 *        \---/    |    \---/      \-----------/
 *          ^      |                   ^  ^
 *          |      |    /---\          |  |
 *   set ----      +--> | I | ---------   |
 *   point         |    \---/             |
 *                 |                      |
 *                 |    /---\             |
 *                 ---> | D | ------------
 *                      \---/
 *
 *
 * Proportional-integral-derivative (PID) controller is made from three parts. 
 *
 * Error which is difference between set point and input signal is driven into
 * propotional, integral and derivative part. Each calculates its own value which
 * is then summed and saturated before given to output.
 *
 * Integral part has also separate input to reset integrator value to 0.
 * 
 */




module red_pitaya_pid_block #(
   parameter     PSR = 12         ,
   parameter     ISR = 18         ,
   parameter     DSR = 10          
)
(
   // data
   input                 clk_i           ,  // clock
   input                 rstn_i          ,  // reset - active low
   input      [ 14-1: 0] dat_i           ,  // input data
   output     [ 14-1: 0] dat_o           ,  // output data

   input 		 pid_hold_pin	 ,  //hold input pin

   // settings
   input      [ 14-1: 0] set_sp_i        ,  // set point
   input      [ 14-1: 0] set_kp_i        ,  // Kp
   input      [ 14-1: 0] set_ki_i        ,  // Ki
   input      [ 14-1: 0] set_kd_i        ,  // Kd
   input                 int_rst_i       ,   // integrator reset
   input                 int_hold_i          // integrator hold
);




//---------------------------------------------------------------------------------
//  Set point error calculation

reg  [ 15-1: 0] error        ;

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      error <= 15'h0 ;
   end
   else begin
      error <= $signed(set_sp_i) - $signed(dat_i) ;
   end
end








//---------------------------------------------------------------------------------
//  Proportional part

reg   [29-PSR-1: 0] kp_reg        ;
wire  [    29-1: 0] kp_mult       ;

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      kp_reg  <= {29-PSR{1'b0}};
   end
   else begin
      kp_reg <= kp_mult[29-1:PSR] ;
   end
end

assign kp_mult = $signed(error) * $signed(set_kp_i);








//---------------------------------------------------------------------------------
//  Integrator

reg   [    29-1: 0] ki_mult       ;
wire  [    33-1: 0] int_sum       ;
reg   [    32-1: 0] int_reg       ;
wire  [32-ISR-1: 0] int_shr       ;

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      ki_mult  <= {29{1'b0}};
      int_reg  <= {32{1'b0}};
   end
   else begin

      if (int_rst_i) begin
         int_reg <= 32'h0; // reset
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else if ((int_hold_i)||(pid_hold_pin)) begin
         int_reg <= int_reg[32-1:0]; //use sum as is
         ki_mult <= {29{1'b0}};
      end 
      else if (int_sum[33-1:33-2] == 2'b01) begin // positive saturation 
         int_reg <= 32'h7FFFFFFF; // max positive
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else if (int_sum[33-1:33-2] == 2'b10)  begin // negative saturation
         int_reg <= 32'h80000000; // max negative
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      else begin
         int_reg <= int_sum[32-1:0]; // use sum as it is
         ki_mult <= $signed(error) * $signed(set_ki_i) ;
      end
      end
end

assign int_sum = $signed(ki_mult) + $signed(int_reg) ;
assign int_shr = int_reg[32-1:ISR] ;




//---------------------------------------------------------------------------------
//  Derivative

wire  [    29-1: 0] kd_mult       ;
reg   [29-DSR-1: 0] kd_reg        ;
reg   [29-DSR-1: 0] kd_reg_r      ;
reg   [29-DSR  : 0] kd_reg_s      ;


always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      kd_reg   <= {29-DSR{1'b0}};
      kd_reg_r <= {29-DSR{1'b0}};
      kd_reg_s <= {29-DSR+1{1'b0}};
   end
   else begin
      kd_reg   <= kd_mult[29-1:DSR] ;
      kd_reg_r <= kd_reg;
      kd_reg_s <= $signed(kd_reg) - $signed(kd_reg_r);
   end
end

assign kd_mult = $signed(error) * $signed(set_kd_i) ;












//---------------------------------------------------------------------------------
//  Sum together - saturate output

wire  [   33-1: 0] pid_sum     ; // biggest posible bit-width
reg   [   14-1: 0] pid_out     ;

always @(posedge clk_i) begin
   if (rstn_i == 1'b0) begin
      pid_out    <= 14'b0 ;
   end
   else begin
      if ({pid_sum[33-1],|pid_sum[32-2:13]} == 2'b01) //positive overflow
         pid_out <= 14'h1FFF ;
      else if ({pid_sum[33-1],&pid_sum[33-2:13]} == 2'b10) //negative overflow
         pid_out <= 14'h2000 ;
      else
         pid_out <= pid_sum[14-1:0] ;
   end
end

assign pid_sum = $signed(kp_reg) + $signed(int_shr) + $signed(kd_reg_s) ;






assign dat_o = pid_out ;



endmodule


Nils Roos
Posts: 1441
Joined: Sat Jun 07, 2014 12:49 pm
Location: Königswinter

Re: PID Integrator Hold

Post by Nils Roos » Sun Oct 26, 2014 12:53 am

Ok, so I can't say for sure that this is the cause of your problem, but there is one thing that could cause unexpected behavior in your design. The external hold inputs come from a different clock domain than the one the pid runs on.

To avoid metastable register states on int_reg and ki_mult, which could be caused by their input changing during the register's clock transition, it is best practice to add a synchronisation stage, see for example the cascade dst_done->sys_sync->sys_done in bus_clk_bridge.v

Code: Select all

reg  [ 2-1: 0] sys_sync  ;
reg            sys_done  ; // <- could also be wires, change below as appropriate
reg            dst_done  ; // _/

always @(posedge sys_clk_i) begin
   if (sys_rstn_i == 1'b0) begin
      sys_sync <= 2'h0 ;
      sys_done <= 1'b0 ;
   end 
   else begin
      sys_sync <= {sys_sync[0], dst_done};
      sys_done <= sys_sync[1];
   end
end
A similar, at least two stages deep shift register should be used to synchronise the pid_hold_pin signals - which are on the clock domain FCLK[0] - before tying them to register logic that is synchronised with adc_clk. The shift register must be clocked by the target's clock, clk_i in your case. There is also a design template for it, search for "asynchronous input".


(just noticed there's an error in the last paragraph which is topic-related, but does not affect the solution. see if you can find it :twisted: )

ClaireE47
Posts: 20
Joined: Thu Oct 02, 2014 8:26 am

Re: PID Integrator Hold

Post by ClaireE47 » Mon Oct 27, 2014 3:44 am

That all sounds great - i'll give it a go.

And errorwise..... are the pid_hold_input signals on clock domain FCLK[0] and FCLK[1]? Because they're exp_p_in[0] and exp_p_in[1]?

Nils Roos
Posts: 1441
Joined: Sat Jun 07, 2014 12:49 pm
Location: Königswinter

Re: PID Integrator Hold

Post by Nils Roos » Mon Oct 27, 2014 10:57 am

And errorwise..... are the pid_hold_input signals on clock domain FCLK[0] and FCLK[1]? Because they're exp_p_in[0] and exp_p_in[1]?
Congrats, you nearly nailed it - certainly had the right idea. The exp_x_in signals are unclocked, as far as I can tell, fully asynchronous, which is even worse than being on a different clock domain.

If you take a look around the scope, you will see that it also employs a synchronisation stage for the external input. I didn't pick it as an example, because it also has edge detection mixed into the same logic.

kstevens
Posts: 3
Joined: Thu Nov 13, 2014 8:55 pm

Re: PID Integrator Hold

Post by kstevens » Thu Nov 13, 2014 9:54 pm

Nils Roos,
I'm also interested in a PID Integrator Hold feature. If it works out for you, please do post the final codes. Thanks!

Nils Roos
Posts: 1441
Joined: Sat Jun 07, 2014 12:49 pm
Location: Königswinter

Re: PID Integrator Hold

Post by Nils Roos » Sat Nov 15, 2014 4:46 am

Oh, this isn't my project, I'm just providing some advice.
But maybe ClaireE47 will let us know how it worked out.

ClaireE47
Posts: 20
Joined: Thu Oct 02, 2014 8:26 am

Re: PID Integrator Hold

Post by ClaireE47 » Tue Nov 25, 2014 7:29 am

Hey,

I haven't been able to get to this for a little while I'm afraid, but I will return to the problem shortly. If I'm able to resolve it, I will definitely post the code I use.

Claire

fromage
Posts: 10
Joined: Wed Jul 02, 2014 9:05 am

Re: PID Integrator Hold

Post by fromage » Tue Nov 25, 2014 1:31 pm

ClaireE47 wrote:Hey,

I haven't been able to get to this for a little while I'm afraid, but I will return to the problem shortly. If I'm able to resolve it, I will definitely post the code I use.

Claire
Hey ClaireE47,
I would also be interested in this feature.
Would be nice if you would share your code once you succeeded ;) .
I can help you in terms of testing your code, but unfortunately I have absolutely no experience in hardware programming and only little in standard coding.
Best regards
Florian

Post Reply
jadalnie klasyczne ekskluzywne meble wypoczynkowe do salonu ekskluzywne meble tapicerowane ekskluzywne meble do sypialni ekskluzywne meble włoskie

Who is online

Users browsing this forum: No registered users and 24 guests