////////////////////////////////////////////////////////////////////////////// // File name : at25320a.v ////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2006 Free Model Foundry; http://www.FreeModelFoundry.com // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // MODIFICATION HISTORY : // // version: | author: | mod date: | changes made: // V1.0 S.Petrovic 06 Mar 29 Initial release // V1.1 R. Munden 06 Nov 27 Correct write // ////////////////////////////////////////////////////////////////////////////// // PART DESCRIPTION: // // Library: RAM // Technology: EEPROM // Part: AT25320A // // Description: 32k (4096 x 8) Serial EEPROM with 5MHz SPI Bus Interface // ////////////////////////////////////////////////////////////////////////////// // Known Bugs: // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // MODULE DECLARATION // ////////////////////////////////////////////////////////////////////////////// `timescale 1 ns/1 ns module at25320a ( SCK , SI , CSNeg , HOLDNeg , WPNeg , SO ); //////////////////////////////////////////////////////////////////////// // Port / Part Pin Declarations //////////////////////////////////////////////////////////////////////// input SCK ; input SI ; input CSNeg ; input HOLDNeg ; input WPNeg ; output SO ; // interconnect path delay signals wire SCK_ipd ; wire SI_ipd ; wire CSNeg_ipd ; wire HOLDNeg_ipd ; wire WPNeg_ipd ; // internal delays reg WR_in ; reg WR_out ; reg SO_zd; reg SO_z; parameter UserPreload = 1; parameter mem_file_name = "none";//"at25320a.mem"; parameter TimingModel = "DefaultTimingModel"; parameter PartID = "at25320a"; parameter MaxData = 255; parameter BlockNum = 3; parameter HiAddrBit = 15; parameter AddrRANGE = 24'hFFF; parameter BYTE = 8; reg WDONE ; //// Writing Done reg WSTART ; ////Start writing //Command Register reg write; reg read_out; //Status reg. reg[7:0] Status_reg = 8'b0; reg[7:0] Status_reg_in = 8'b0; integer Byte_number = 0; //Address integer Address = 0; // 0 - AddrRANGE reg change_addr; //Sector Protection Status reg [BlockNum:0] Block_Prot; //= BlockNum'b0; // timing check violation reg Viol = 1'b0; integer Mem[0:AddrRANGE]; integer WByte[0:31]; integer AddrLo; integer AddrHi; reg[7:0] old_bit, new_bit; integer old_int, new_int; integer wr_cnt; integer read_cnt = 0; integer read_addr = 0; reg[7:0] data_out; reg oe = 1'b0; event oe_event; /////////////////////////////////////////////////////////////////////////////// //Interconnect Path Delay Section /////////////////////////////////////////////////////////////////////////////// buf (SCK_ipd, SCK); buf (SI_ipd, SI); buf (CSNeg_ipd, CSNeg); buf (HOLDNeg_ipd, HOLDNeg); buf (WPNeg_ipd, WPNeg); /////////////////////////////////////////////////////////////////////////////// // Propagation delay Section /////////////////////////////////////////////////////////////////////////////// nmos (SO, SO_z, 1); specify // tipd delays: interconnect path delays , mapped to input port delays. // In Verilog is not necessary to declare any tipd_ delay variables, // they can be taken from SDF file // With all the other delays real delays would be taken from SDF file // tpd delays specparam tpd_SCK_SO =1; specparam tpd_CSNeg_SO =1; specparam tpd_HOLDNeg_SO =1; specparam tsetup_SI_SCK =1; //tSU / specparam tsetup_CSNeg_SCK =1; // tCSS / specparam tsetup_HOLDNeg_SCK =1; //tHD / // thold values: hold times specparam thold_SI_SCK =1; //tH \ specparam thold_CSNeg_SCK =1; //tCSH \ specparam thold_HOLDNeg_SCK =1; //tCD \ // tpw values: pulse width specparam tpw_SCK_posedge =1; //tWH specparam tpw_SCK_negedge =1; //tWL specparam tpw_CSNeg_posedge =1; //tCS // tperiod min (calculated as 1/max freq) specparam tperiod_SCK =1; // fSCK = 5MHz // tdevice values: values for internal delays //Write cycle time specparam tdevice_WR = 5000000; // 5 ms; /////////////////////////////////////////////////////////////////////////////// // Input Port Delays don't require Verilog description /////////////////////////////////////////////////////////////////////////////// // Path delays // /////////////////////////////////////////////////////////////////////////////// (SCK => SO) = tpd_SCK_SO; (CSNeg => SO) = tpd_CSNeg_SO; (HOLDNeg => SO) = tpd_HOLDNeg_SO; //////////////////////////////////////////////////////////////////////////////// // Timing Violation // //////////////////////////////////////////////////////////////////////////////// $setup ( negedge HOLDNeg, posedge SCK, tsetup_HOLDNeg_SCK, Viol); $setup ( posedge HOLDNeg, posedge SCK, tsetup_HOLDNeg_SCK, Viol); $setup ( negedge CSNeg, posedge SCK, tsetup_CSNeg_SCK, Viol); $hold ( negedge SCK, negedge HOLDNeg, thold_HOLDNeg_SCK, Viol); $hold ( negedge SCK, posedge HOLDNeg, thold_HOLDNeg_SCK, Viol); $hold ( negedge SCK, CSNeg, thold_CSNeg_SCK, Viol); $setuphold ( posedge SCK, SI, tsetup_SI_SCK, thold_SI_SCK, Viol); $width (posedge SCK, tpw_SCK_posedge); $width (negedge SCK, tpw_SCK_negedge); $width (posedge CSNeg, tpw_CSNeg_posedge); $period (posedge SCK, tperiod_SCK); endspecify //////////////////////////////////////////////////////////////////////////////// // Main Behavior Block // //////////////////////////////////////////////////////////////////////////////// // FSM states parameter IDLE =3'd0; parameter WRITE_SR =3'd1; parameter PAGE_PG =3'd2; reg [2:0] current_state; reg [2:0] next_state; // Instructions parameter NONE =4'd0; parameter WREN =4'd1; parameter WRDI =4'd2; parameter WRSR =4'd3; parameter RDSR =4'd4; parameter READ =4'd5; parameter PP =4'd6; reg [3:0] Instruct; //Bus cycle states parameter STAND_BY =3'd0; parameter CODE_BYTE =3'd1; parameter ADDRESS_BYTES =3'd2; parameter DATA_BYTES =3'd3; reg [2:0] bus_cycle_state; initial begin : Init write = 1'b0; read_out = 1'b0; Address = 0; change_addr = 1'b0; WDONE = 1'b1; WSTART = 1'b0; Instruct = NONE; bus_cycle_state = STAND_BY; current_state = IDLE; next_state = IDLE; end // initialize memory initial begin: InitMemory integer i; for (i=0;i<=AddrRANGE;i=i+1) begin Mem[i] = MaxData; end if ((UserPreload) && !(mem_file_name == "none")) begin // Memory Preload //at25320a.mem, memory preload file // @aaa - stands for address // dd -
is byte to be written at Mem(aaa++) // (aaa is incremented at every load) $readmemh(mem_file_name,Mem); end end always @(next_state) begin: StateTransition current_state = next_state; end // /////////////////////////////////////////////////////////////////////////// // // Instruction cycle decode // /////////////////////////////////////////////////////////////////////////// integer data_cnt = 0; integer addr_cnt = 0; integer code_cnt = 0; integer bit_cnt = 0; reg[255:0] Data_in = 256'b0; reg[7:0] code = 8'b0; reg[7:0] code_in = 8'b0; reg[7:0] Byte_slv = 8'b0; reg[HiAddrBit:0] addr_bytes; reg[15:0] Address_in = 16'b0; always @(negedge CSNeg_ipd) begin: Buscycle1 if (bus_cycle_state==STAND_BY) begin bus_cycle_state = CODE_BYTE; Instruct = NONE; write = 1'b1; code_cnt = 0; addr_cnt = 0; data_cnt = 0; end end always @(posedge SCK_ipd) begin: Buscycle2 integer i; if ( HOLDNeg_ipd) begin case (bus_cycle_state) CODE_BYTE : begin code_in[code_cnt] = SI_ipd; code_cnt = code_cnt + 1; if (code_cnt == BYTE) begin for (i=0;i<=7;i=i+1) begin code[i] = code_in[7-i]; end case(code) 8'b00000110 : begin Instruct = WREN; bus_cycle_state = DATA_BYTES; end 8'b00000100 : begin Instruct = WRDI; bus_cycle_state = DATA_BYTES; end 8'b00000001 : begin Instruct = WRSR; bus_cycle_state = DATA_BYTES; end 8'b00000101 : begin Instruct = RDSR; bus_cycle_state = DATA_BYTES; end 8'b00000011 : begin Instruct = READ; bus_cycle_state = ADDRESS_BYTES; end 8'b00000010 : begin Instruct = PP; bus_cycle_state = ADDRESS_BYTES; end endcase end end ADDRESS_BYTES : begin Address_in[addr_cnt] = SI_ipd; addr_cnt = addr_cnt + 1; if (addr_cnt == 2*BYTE) begin for (i=15;i>=0;i=i-1) begin addr_bytes[15-i] = Address_in[i]; end Address = addr_bytes; change_addr = 1'b1; #1 change_addr = 1'b0; bus_cycle_state = DATA_BYTES; end end DATA_BYTES : begin if (data_cnt > 255) //In case of PP, if more than 32 bytes are //sent to the device begin if (bit_cnt == 0) begin for (i=0;i<=(31*BYTE-1);i=i+1) begin Data_in[i] = Data_in[i+8]; end end Data_in[248 + bit_cnt] = SI_ipd; bit_cnt = bit_cnt + 1; if (bit_cnt == 8) begin bit_cnt = 0; end data_cnt = data_cnt + 1; end else begin Data_in[data_cnt] = SI_ipd; data_cnt = data_cnt + 1; bit_cnt = 0; end end endcase end end always @(negedge SCK_ipd) begin: Buscycle3 if (bus_cycle_state==DATA_BYTES && (~CSNeg_ipd) && (HOLDNeg_ipd)) if (Instruct == READ || Instruct == RDSR) read_out = 1'b1; #1 read_out = 1'b0; end always @(posedge CSNeg_ipd) begin: Buscycle4 integer i; integer j; if (bus_cycle_state == DATA_BYTES) begin bus_cycle_state = STAND_BY; if (HOLDNeg_ipd) begin case (Instruct) WREN, WRDI: begin if (data_cnt == 0) write = 1'b0; end WRSR : begin if (data_cnt == BYTE) write = 1'b0; Status_reg_in = Data_in[7:0]; end PP : begin if ((data_cnt >= BYTE) && ((data_cnt % 8) == 0)) begin write = 1'b0; for (i=0;i<=31;i=i+1) begin for (j=7;j>=0;j=j-1) begin Byte_slv[j] = Data_in[(i*8) + (7-j)]; end WByte[i] = Byte_slv; end if (data_cnt > 32*BYTE) Byte_number = 31; else Byte_number = ((data_cnt/8) - 1); end end endcase end end else bus_cycle_state = STAND_BY; end // ///////////////////////////////////////////////////////////////////////// // // Timing control for the Write Operation start // ///////////////////////////////////////////////////////////////////////// event wdone_event; always @(WSTART) begin if (WSTART && WDONE) begin WDONE = 1'b0; ->wdone_event; end end always @(wdone_event) begin:wdone_process WDONE = 1'b0; #tdevice_WR WDONE = 1'b1; end // ///////////////////////////////////////////////////////////////////////// // // Main Behavior Process // // combinational process for next state generation // ///////////////////////////////////////////////////////////////////////// integer blck; always @(negedge write) begin: StateGen1 if (current_state == IDLE) begin if (~write) begin if (Instruct == WRSR && Status_reg[1]) begin if (~(Status_reg[7] && (~WPNeg_ipd))) next_state = WRITE_SR; end else if (Instruct == PP && Status_reg[1]) begin blck = Address / 16'h400; if (Block_Prot[blck] == 1'b0) next_state = PAGE_PG; end else next_state = IDLE; end end end always @(posedge WDONE) begin: StateGen2 if (current_state==WRITE_SR || current_state==PAGE_PG) begin next_state = IDLE; end end /////////////////////////////////////////////////////////////////////////// //FSM Output generation and general funcionality /////////////////////////////////////////////////////////////////////////// always @(posedge read_out) begin ->oe_event; end always @(oe_event) begin oe = 1'b1; #1 oe = 1'b0; end always @(Instruct) begin read_cnt = 0; end always @(change_addr) begin if (change_addr) read_addr = Address; end always @(oe or current_state) begin case (current_state) IDLE : begin if (oe) begin if (Instruct == RDSR) begin //Read Status Register SO_zd = Status_reg[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end else if (Instruct == READ) begin //Read Memory array data_out[7:0] = Mem[read_addr]; SO_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) begin read_cnt = 0; if (read_addr == AddrRANGE) read_addr = 0; else read_addr = read_addr + 1; end end end end WRITE_SR, PAGE_PG : begin if (oe && Instruct == RDSR) SO_zd = 1'b1; end endcase end integer prot_bl; integer WData [0:31]; integer Addr; always @(negedge write) begin : Output_generation integer i; if (current_state == IDLE) begin if (~write) begin if (Instruct == WREN) Status_reg[1] = 1'b1; else if (Instruct == WRDI) Status_reg[1] = 1'b0; else if (Instruct == WRSR && Status_reg[1] && (~(Status_reg[7] == 1'b1 && WPNeg_ipd == 1'b0))) begin WSTART = 1'b1; WSTART <= #1 1'b0; Status_reg[0] = 1'b1; end else if (Instruct == PP && Status_reg[1] == 1'b1) begin prot_bl = Address / 16'h400; if (Block_Prot[prot_bl] == 1'b0) begin WSTART = 1'b1; WSTART <= #1 1'b0; Status_reg[0] = 1'b1; Addr = Address; wr_cnt = Byte_number; for (i=0;i<=wr_cnt;i=i+1) begin if (Viol!=1'b0) WData[i] = -1; else WData[i] = WByte[i]; end end end end end end always @(current_state or WDONE) begin: WRITE integer i, j; if (current_state == WRITE_SR) begin if (WDONE) begin Status_reg[0] = 1'b0;//RDY Status_reg[1] = 1'b0;//WEN Status_reg[7] = Status_reg_in[0];//MSB first, WPEN Status_reg[3] = Status_reg_in[4];//MSB first, BP1 Status_reg[2] = Status_reg_in[5];//MSB first, BP0 Status_reg[6:4] = 3'b000; case (Status_reg[3:2]) 2'b00 : begin Block_Prot = 4'b0000; end 2'b01 : begin Block_Prot = 4'b1000; end 2'b10 : begin Block_Prot = 4'b1100; end 2'b11 : begin Block_Prot = 4'b1111; end endcase end end else if (current_state == PAGE_PG) begin ADDRHILO_PG(AddrLo, AddrHi, Addr); if ((Addr + wr_cnt) > AddrHi) wr_cnt = AddrHi - Addr; for (i=Addr;i<=Addr+wr_cnt;i=i+1) begin Mem[i] = -1; end if (WDONE) begin Status_reg[0] = 1'b0;//rdy Status_reg[1] = 1'b0;// wen for (i=Addr;i<=Addr+wr_cnt;i=i+1) begin Mem[i] = WData[i-Addr]; end end end end // Output Control always @(CSNeg_ipd or HOLDNeg_ipd or SCK_ipd) begin //Output Disable Control if (CSNeg_ipd ) SO_zd = 1'bZ; end always @(SO_zd or HOLDNeg_ipd) begin if (HOLDNeg_ipd == 1) SO_z = SO_zd; else SO_z = 1'bZ; end // Procedure ADDRHILO_PG task ADDRHILO_PG; inout AddrLOW; inout AddrHIGH; input Addr; integer AddrLOW; integer AddrHIGH; integer Addr; integer page; begin page = Addr / 10'h20; AddrLOW = page * 10'h20; AddrHIGH = page * 10'h20 + 10'h1F; end endtask endmodule