FPGAでADCを制御する

#前置き
A/D Converter制御用コードを作成したので載せます。
今回使用したICはAD7982です。
変換時間はadc_tconv，回路遅延によるデータ取得の同期ずれはadc_taqcで調整してます。
あとは普通のSPIです。

```AD7982.vhd
library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity ad7982 is
port(
        clk:    in  std_logic;
        cnv_st: in  std_logic;
        cnv_ed: out std_logic;
        dres:   out std_logic_vector(17 downto 0);
        --To AD5542
        sdi:    out std_logic;
        sdo:    in  std_logic;
        cnv:    out std_logic;
        sclk:   out std_logic
    );
end ad7982;

Architecture RTL of ad7982 is

signal   pclk:          std_logic :='0';
constant pclk_cnt_bit:  integer := 4;
constant pclk_cnt_max:  std_logic_vector(pclk_cnt_bit -1 downto 0) :=x"e";
signal   pclk_cnt:      std_logic_vector(pclk_cnt_bit -1 downto 0) :=(others=>'0');

constant adc_bit:       integer :=18;
constant adc_rectime:   std_logic_vector(5 downto 0) := "011000"; --24
constant adc_cnt_zero:  std_logic_vector(5 downto 0) := (others=>'0'); --0
constant adc_tconv:     std_logic_vector(5 downto 0) := "111111"; --63
constant adc_taqc:      std_logic_vector(5 downto 0) := "010011"; --19
signal   adc_cnv:       std_logic :='0';
signal   adc_cnvcnt:    std_logic_vector(5 downto 0) := (others=>'0'); --0
signal   adc_cnvend:    std_logic :='0';
signal   adc_data:      std_logic_vector(adc_bit-1 downto 0) :=(others=>'0');
signal   adc_data_spl:  std_logic_vector(adc_bit-1 downto 0) :=(others=>'0');
signal   adc_streamcnt: std_logic_vector(5 downto 0) := (others=>'0');
signal   adc_streamend: std_logic :='0';
signal   adc_start:     std_logic :='0';

signal sclk_in: std_logic :='0';
signal cnv_in:  std_logic :='0';
signal sdi_in:  std_logic :='0';
signal rec_ed:  std_logic :='0';

--state machine
signal adc_state:   std_logic_vector(1 downto 0) :=(others=>'0');
    --state 0: wait;
    --state 1: change a clock source from main one to divided one;
    --state 2: conv start;
    --state 3: data acquistion;

begin

--to ad5542a
sclk <= sclk_in;
sdi<=sdi_in;
cnv<=adc_cnv;
dres<=adc_data_spl;
cnv_ed<=rec_ed;

--From master system 
adc_start <= cnv_st;

--Clock divider
process(clk) begin
    if clk'event and clk='1' then
        --Timing manager for clock generator
        if pclk_cnt=pclk_cnt_max then pclk_cnt <= (others=>'0');
        else pclk_cnt <= pclk_cnt+'1'; end if;
        --Clock generator
        if pclk_cnt=pclk_cnt_max then pclk <= not pclk;
        else pclk <= pclk; end if;
    end if;
end process;

--State machine
process(clk) begin
    if clk'event and clk='1' then
        case adc_state is
            when "00" =>
                if adc_start='1' then adc_state <="01";
                else adc_state <= adc_state; end if;
            when "01" =>
                if adc_cnvend='1' then adc_state <="10";
                else adc_state <= adc_state; end if;
            when "10" =>
                if pclk_cnt=pclk_cnt_max and pclk='1' then adc_state <= "11";
                else adc_state <= adc_state; end if;
            when "11" =>
                if rec_ed='1' then adc_state <="00";
                else adc_state <= adc_state; end if;
            when others =>
                adc_state <= adc_state;
        end case;
        
        
    end if;
end process;

--Communicator
process(clk) begin
    if clk'event and clk='1' then
        

        --Conv start
        if adc_state="01" then
            --Stream Data
            if adc_cnvcnt="000001" then adc_cnv <= '1';
            elsif adc_cnvcnt=adc_tconv then adc_cnv <= '0';
            else adc_cnv<=adc_cnv; end if;
            --Stream Count
            if adc_cnvcnt=adc_tconv then adc_cnvcnt<=(others=>'0');
            else adc_cnvcnt <= adc_cnvcnt+'1'; end if;
            --Conv End Flag
            if adc_cnvcnt=adc_tconv then adc_cnvend<='1';
            else adc_cnvend <= '0'; end if;
        else
            adc_cnv<='0';
            adc_cnvcnt <= (others=>'0');
            adc_cnvend <= '0';
        end if;

        --Data acquisition
        if pclk_cnt=pclk_cnt_max and pclk='1' then
            if adc_state="11" then
                if adc_streamcnt=adc_cnt_zero then adc_data <= (others=>'0');
                else adc_data <= adc_data(adc_bit-2 downto 0) & sdo; end if;
            else adc_data <= (others=>'0'); end if;
            if adc_streamcnt=adc_taqc then adc_data_spl<=adc_data; else adc_data_spl<=adc_data_spl; end if;
        end if;
                
        --Data acquisition
        if pclk_cnt=pclk_cnt_max and pclk='1' then
            if adc_state="11" then
                --Stream Count
                if adc_streamcnt=adc_rectime then adc_streamcnt<=(others=>'0');
                else adc_streamcnt <= adc_streamcnt+'1'; end if;
                --Stream End Flag
                if adc_streamcnt=adc_rectime then adc_streamend<='1';
                else adc_streamend <= '0'; end if;
                --Receive end
                if adc_streamcnt=adc_rectime then rec_ed<='1';
                else rec_ed<='0'; end if;
            else
                adc_streamcnt <= (others=>'0');
                adc_streamend <= '0';
                rec_ed<='0';
            end if;
        end if;
        
    end if;
end process;

sclk_in <= pclk  when adc_state ="11" else '0';
sdi_in <= '1'    when adc_state/="00" else '0';

end RTL;
```

```AD7982.vhd
module ad7982(clk,cnv_st,dres,sdo,sdi,sclk,conv);

input clk,cnv_st;
output [17:0] dres;
input sdo;
output sdi,sclk,conv;

reg 		pclk;	initial pclk=0;
reg [3:0]	pcnt; 	initial pcnt=2'b0;
parameter	pmax=4'h4;

always@(posedge clk)
begin
	pcnt<=(pcnt==pmax)? 4'h0:pcnt+4'h1;
	pclk<=(pcnt==pmax)? ~pclk:pclk;
end

reg [1:0]	state;		initial state=2'b0;
reg	[6:0] 	convcnt;	initial convcnt=7'b0;
reg 		convend;	initial convend=1'b0;
parameter [6:0]	convtime=7'd75;
reg [4:0]	reccnt; 	initial reccnt=5'b0;
reg 		recend;		initial recend=1'b0;
reg [17:0]	recdata;	initial recdata=18'b0;
reg [17:0]	getdata;	initial getdata=18'b0;
parameter [4:0]	rectime=5'd22;


assign sdi=(state!=2'b00)? 1'b1:1'b0;
assign conv=(convcnt!=7'd0&convcnt!=convtime)? 1'b1:1'b0;
assign sclk=(state==2'b11)? pclk:1'b0;
assign dres=getdata;

always@(posedge clk)
begin
	case(state)
		2'b00: state<=(cnv_st==1'b1)? state+2'b1:state;
		2'b01: state<=(convend==1'b1)? state+2'b1:state;
		2'b10: state<=(pcnt==pmax&pclk==1'b1)? state+2'b1:state;
		2'b11: state<=(recend==1'b1)? state+2'b1:state;
		default: state=2'b00;
	endcase	
end

always@(posedge clk)
begin
	
	if(state==2'b01)
	begin
		convcnt<=(convcnt==convtime)? convcnt:convcnt+7'd1;
		convend<=(convcnt==convtime)? 1'b1:1'b0;
	end
	else begin convcnt<=7'd0; convend<=1'b0; end
	
	if(pcnt==pmax&pclk==1'b1)
	begin		
		if(state==2'b11)
		begin
			reccnt<=(reccnt==rectime)? reccnt:reccnt+5'b1;
			recend<=(reccnt==rectime)? 1'b1:1'b0;
		end
		else begin reccnt<=5'd0; recend<=1'b0; end 
	end	
	
	if(pcnt==pmax&pclk==1'b1)
	begin		
		if(state==2'b11)
		begin
			recdata<=(reccnt==5'd0)? 18'b0:{recdata[16:0],sdo};
			getdata<=(reccnt==5'd19)? recdata:getdata;
		end
		else begin recdata<=18'b0; getdata<=getdata; end
	end	

end

endmodule
```