본문 바로가기

VERILOG

ZCU102-G + AD7606C18 Simultaneous Sampling ADC 8ch 18Bit -1 parallel

ZCU102-G 보드와

EVAL-AD7606CFMCZ 보드

 

를 연결하여 아날로그 신호 8채널을 읽어서 ADC 변환하고 디지털신호를 아날로그 신호로 파형을 확인하는 것을 해보려고 합니다.

 

FMC는 "FPGA Mezzanine Card"의 약자로, FPGA와 연결되는 확장 카드 규격 중 하나

 

우선은 FMC가 동일한지 확인을 하려고 각 보드의 FMC 핀을 확인하였는데

결론은 FMC는 동일한 규격이라 확인할 필요는 없었습니다.

 

우선은 EVAL-AD7606CFMCZ 보드의 FMC 핀 사진을 보시면 핀이 4개가 있는 것이 육안으로 보입니다. 

 

EVAL-AD7606CFMCZ 보드의 FMC핀을 보면 C, D, G, H 로 되어있는 것을 확인할 수 있고

 

ZCU102-G 보드를 보면 HPC0, HPC1 로 2개의 FMC포트가 있는 것을 확인할 수 있고 핀이 10개가 있는 것이 육안으로 보입니다.

ZCU102-G 보드는 A~K 까지 있는 것을 확인할 수 있고 데이터시트도 확인합니다.

 

FMC가 동일한 핀으로 구성되어 있는 것을 확인할 수 있고 이것들은 추후에 XDC파일에 핀을 대조하여 연결하기 위해서도 필요합니다.

 

여담으로 ZCU102-G 보드는 XILINX ZYNQ ULTRASCALE+ MPSOC 제품으로 XCZU9EG 칩을 사용하는데 일반 라이센스를 가지고는 이 칩을 비트스트림 할 수 없습니다. 라이센스가 필요합니다.

  • [Common 17-345] A valid license was not found for feature 'Synthesis' and/or device 'xczu9eg'. Please run the Vivado License Manager for assistance in determining which features and devices are licensed for your system. Resolution: Check the status of your licenses in the Vivado License Manager. For debug help search Xilinx Support for "Licensing FAQ". If you are using a license server, verify that the license server is up and running a version of the xilinx daemon that is compatible with the version of Xilinx software that you are using. Note: Vivado 2021.1 and later versions require upgrading your license server tools to the Flex 11.17.2.0 versions. Please confirm with your license admin that the correct version of the license server tools are installed.

해당 에러가 발생합니다.

 

정확하게 말씀드리면 XCZU7EG 이하의 제품들은 일반 라이센스로 사용이 가능합니다.

 

관련하여 블로그에 따로 글을 작성하였습니다.

 

FMC로 보드가 연결될 수 있음을 확인하였습니다.

 

우선은 Datasheet상의  EVAL-AD7606CFMCZ 보드의 칩인 AD7606C-18의 PIN의 동작들을 확인합니다.

 

 

등등 확인하고 데이터를 읽은 2가지 방법 serial 직렬로 읽을지 parallel 병렬로 읽을지 선택합니다.

 

시리얼은 코드짜기도 길고 복잡하여

페러럴로 ch1~ch8 까지 동시에 읽는 것이 코드 짜기에 적합하다고 판단했습니다.

 

 

타이밍 다이어그램을 참고하여 ad7606c18 구동 코드를 짜줍니다.

 

module ad7606c18(
   input          clk,              // 50MHz 클럭
   input          rst_n,            // 리셋 신호 (Active Low)

   input [15:0]   ad_data,          // AD7606에서 변환된 아날로그 데이터
   input          ad_busy,          // AD 변환이 완료되었는지 여부를 나타내는 신호
   input          first_data,       // AD 변환 시작 시 초기화를 나타내는 신호

   output [2:0]   ad_os,            // AD7606의 오버샘플링 모드 설정 (항상 000)
   output reg     ad_cs,            // AD7606 Chip Select
   output reg     ad_rd,            // AD7606 AD 데이터 읽기
   output reg     ad_reset,         // AD7606 AD 리셋
   output reg     ad_convstab,      // AD7606 AD 변환 시작
   output reg     [4:0] state,

   output reg [17:0] ad_ch1,         // AD 채널 1 데이터 (18비트로 수정)
   output reg [17:0] ad_ch2,         // AD 채널 2 데이터 (18비트로 수정)
   output reg [17:0] ad_ch3,         // AD 채널 3 데이터 (18비트로 수정)
   output reg [17:0] ad_ch4,         // AD 채널 4 데이터 (18비트로 수정)
   output reg [17:0] ad_ch5,         // AD 채널 5 데이터 (18비트로 수정)
   output reg [17:0] ad_ch6,         // AD 채널 6 데이터 (18비트로 수정)
   output reg [17:0] ad_ch7,         // AD 채널 7 데이터 (18비트로 수정)
   output reg [17:0] ad_ch8          // AD 채널 8 데이터 (18비트로 수정)
);

reg [16:0] cnt;   // 카운터 레지스터
reg [5:0] i;       // 인덱스 레지스터


parameter IDLE=      0;
parameter AD_CONV=   1;
parameter Wait_1=    2;
parameter Wait_busy= 3;
parameter READ_CH1_1=4;
parameter READ_CH1_2=5;
parameter READ_CH2_1=6;
parameter READ_CH2_2=7;
parameter READ_CH3_1=8;
parameter READ_CH3_2=9;
parameter READ_CH4_1=10;
parameter READ_CH4_2=11;
parameter READ_CH5_1=12;
parameter READ_CH5_2=13;
parameter READ_CH6_1=14;
parameter READ_CH6_2=15;
parameter READ_CH7_1=16;
parameter READ_CH7_2=17;
parameter READ_CH8_1=18;
parameter READ_CH8_2=19;
parameter READ_DONE= 20;

assign ad_os=3'b000;  // AD7606의 오버샘플링 모드 항상 000으로 설정

// AD 레지스터 초기화
always @(posedge clk)
begin
   if(cnt < 16'hffff) begin
      cnt <= cnt + 1;
      ad_reset <= 1'b1;
   end
   else
      ad_reset <= 1'b0;
end

// AD7606 동작 상태 머신
always @(posedge clk)
begin
   if(ad_reset == 1'b1) begin
      // 리셋 시 초기화
      state <= IDLE;
      ad_ch1 <= 0;
      ad_ch2 <= 0;
      ad_ch3 <= 0;
      ad_ch4 <= 0;
      ad_ch5 <= 0;
      ad_ch6 <= 0;
      ad_ch7 <= 0;
      ad_ch8 <= 0;
      ad_cs <= 1'b1;
      ad_rd <= 1'b1;
      ad_convstab <= 1'b1;
      i <= 0;
   end
   else begin
      // 상태 머신 동작
      case(state)
         IDLE: begin
            // IDLE 상태에서 초기화를 위해 20 클럭 동안 대기
            ad_cs <= 1'b1;
            ad_rd <= 1'b1;
            ad_convstab <= 1'b0;
            if(i == 20) begin
               i <= 0;
               state <= AD_CONV;
            end
            else
               i <= i + 1'b1;
         end
         AD_CONV: begin
            // AD 변환 시작 후 2 클럭 동안 대기
            if(i == 2) begin
               i <= 0;
               state <= Wait_1;
               ad_convstab <= 1'b1;
            end
            else begin
               i <= i + 1'b1;
               ad_convstab <= 1'b0;
            end
         end
         Wait_1: begin
            // 5 클럭 대기 후 AD 변환이 완료되면 다음 단계로
            if(i == 5) begin
               i <= 0;
               state <= Wait_busy;
            end
            else
               i <= i + 1'b1;
         end
         Wait_busy: begin
            // AD 변환이 완료되면 다음 단계로
            if(ad_busy == 1'b0) begin
               i <= 0;
               state <= READ_CH1_1;
            end
         end
         READ_CH1_1: begin
            // 각 채널별로 데이터 읽기, 6 클럭 동안 대기
            if(i == 3) begin
               ad_rd <= 1'b1;
               i <= 0;
               ad_ch1[17:2] <= {ad_data[15:00]}; 
               ad_cs <= 1'b1;
               state <= READ_CH1_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH1_2: begin
            // 채널 2 데이터 읽기
            if(i == 3) begin
               ad_ch1[1:0] <= {ad_data[0], ad_data[1]};
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               state <= READ_CH2_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH2_1: begin
            // 채널 3 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch2[17:2] <= {ad_data[15:00]}; 
               state <= READ_CH2_2;
            end
            else begin
               ad_rd <= 1'b0;
               ad_cs <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH2_2: begin
            // 채널 4 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch2[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_CH3_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH3_1: begin
            // 채널 5 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch3[17:2] <= {ad_data[15:00]}; 
               state <= READ_CH3_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH3_2: begin
            // 채널 6 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch3[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_CH4_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH4_1: begin
            // 채널 7 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch4[17:2] <= {ad_data[15:00]}; 
               state <= READ_CH4_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH4_2: begin
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch4[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_CH5_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH5_1: begin
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch5[17:2] <= {ad_data[15:00]}; 
               state <= READ_CH5_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH5_2: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               i <= 0;
               ad_ch5[1:0] <= {ad_data[0], ad_data[1]};
               ad_cs <= 1'b1;
               state <= READ_CH6_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH6_1: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch6[17:2] <= {ad_data[15:00]}; 
               state <= READ_CH6_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH6_2: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch6[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_CH7_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH7_1: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch7[17:2] <= {ad_data[15:00]};  
               state <= READ_CH7_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH7_2: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch7[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_CH8_1;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH8_1: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch8[17:2] <= {ad_data[15:00]};  
               state <= READ_CH8_2;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_CH8_2: begin
            // 채널 8 데이터 읽기
            if(i == 3) begin
               ad_rd <= 1'b1;
               ad_cs <= 1'b1;
               i <= 0;
               ad_ch8[1:0] <= {ad_data[0], ad_data[1]};
               state <= READ_DONE;
            end
            else begin
               ad_cs <= 1'b0;
               ad_rd <= 1'b0;
               i <= i + 1'b1;
            end
         end
         READ_DONE: begin
            // 데이터 읽기 완료 후 IDLE 상태로
            ad_cs <= 1'b1;
            ad_rd <= 1'b1;
            ad_convstab <= 1'b1;
            state <= IDLE;
         end
      endcase
   end
end

endmodule

 

코드를 구성하고 XDC를 연결해줍니다.

AD7606C18 FMC PIN을 봤을 때 DB0은 LA01_P_CC에 연결되고

 

 

ZCU102-G 보드의 데이터시트에 

 

FMC_HPC1_LA01_CC_P 에 연결된 것을 확인할 수 있고 이 핀은 XCZU9EG핀에서

 

AJ6에 연결된 것을 확인할 수 있습니다.

 

XDC파일에

 

화면에 보이는 것처럼 XDC 선언을 해줍니다.

 

xdc 선언을 해주다가 clk 은 어디서 받아야되는지 혼란이 왔습니다.

 

clk 선언은 2번째 글부터 진행하겠습니다.