//
// Copyright (c) 2014-2022 by 1801BM1@gmail.com
//
// F-11 asynchronous model with Wishbone frontend bus.
//
//______________________________________________________________________________
//
module f11_wb
#(parameter
//______________________________________________________________________________
//
// F11_CORE_MMU enables code MMU generation in the dc304 module
// F11_CORE_FPP enables code of FPP MiCROM in the dc303 module
//
   F11_CORE_MMU = 1,
   F11_CORE_FPP = 1
)
(
   //
   // Processor core clock section:
   //    - vm_clk_p     - processor core positive clock, also feeds the wishbone buses
   //    - vm_clk_n     - processor core negative clock, should be vm_clk_p 180 degree phase shifted
   //    - vm_clk_ena   - slow clock simulation strobe, enables clock at vm_clk_p
   //    - vm_clk_slow  - clock mode selector, enables clock slowdown simulation,
   //                     the external I/O cycles is launched with rate of vm_clk_ena
   //
   input          vm_clk_p,      // positive edge clock
   input          vm_clk_n,      // negative edge clock
   input          vm_clk_ena,    // slow clock enable
   input          vm_clk_slow,   // slow clock sim mode
                                 //
   output         vm_init,       // peripheral reset output
   input          vm_dclo,       // processor reset
   input          vm_aclo,       // power fail notificaton
   input          vm_halt,       // halt mode interrupt
   input          vm_evnt,       // timer interrupt requests
   input  [7:4]   vm_virq,       // vectored interrupt request
                                 //
   input          wbm_gnt_i,     // master wishbone granted
   output         wbm_ios_o,     // master wishbone bank I/O select
   output [21:0]  wbm_adr_o,     // master wishbone address
   output [15:0]  wbm_dat_o,     // master wishbone data output
   input  [15:0]  wbm_dat_i,     // master wishbone data input
   output         wbm_cyc_o,     // master wishbone cycle
   output         wbm_we_o,      // master wishbone direction
   output [1:0]   wbm_sel_o,     // master wishbone byte select
   output         wbm_stb_o,     // master wishbone strobe
   input          wbm_ack_i,     // master wishbone acknowledgement
                                 //
   input  [15:0]  wbi_dat_i,     // interrupt vector input
   input          wbi_ack_i,     // interrupt vector acknowledgement
   output         wbi_stb_o,     // interrupt vector strobe
   output         wbi_una_o,     // unaddressed fast input read
                                 //
   output         vm_umap,       // enable dma address translation
   input [1:0]    vm_bsel        // boot mode selector
);

//______________________________________________________________________________
//
wire  mce_p;                     // enable positive (rising) mclk edge
wire  mce_n;                     // enable negative (falling) mclk edge
reg   mclk;                      // master CPU clock
reg   mme0, mme1;                // MMU address translation phases
reg   qrdy;                      // bus ready for transaction
reg   drdy;                      // data ready to complete
                                 //
wire  [15:0] mo;                 // early microbus status/command
reg   [15:0] mc;                 // latched microbus status/command
                                 //
wire  [15:0] m;                  // microinstruction bus
wire  [15:0] ad;                 // shared address/data bus
wire  [15:0] ad_cpu;             // CPU address/data output
wire  [15:0] ad_mmu;             // MMU address/data output
reg   [21:0] wb_adr;             // wishbone output address
reg   [15:0] wb_dat;             // wishbone input data
reg   [1:0]  wb_sel;             //
                                 //
wire         wb_wclr, wb_wset;   //
reg          wb_swait;           //
reg   [5:0]  wb_wcnt;            //
                                 //
reg   wb_ios;                    // I/O bank select
reg   wb_stb;                    //
reg   wb_we;                     //
reg   wb_bcyc;                   // master bus cycle
reg   wb_iako;                   // read interrupt vector
reg   wb_fdin;                   // read fast data input
                                 //
wire  iow_req;                   // data write bus request
wire  ior_req;                   // data read bus request
wire  io_wtbt;                   // write byte request
wire  wb_wdone;                  //
wire  wb_rdone;                  //
wire  wb_idone;                  //
                                 //
wire  [21:16] at_mmu;            // MMU translated address
wire  [2:0] pga;                 // page address for MMU
wire  bs_cpu, bs_mmu, bsio;      // I/O bank select logic
                                 //
wire  doe;                       //
wire  adr_stb;                   // latch bus address
reg   wdat_oe;                   // enable latched data
reg   fdin_oe;                   //
                                 //
wire  umap;                      // upper addresses mapping enable
wire  abort;                     // abort bus cycle (error/timeout)
reg   mc_res;                    // reset control chip/CPU
wire  mr_sel;                    // MMU register selected
wire  io_psw;                    // PSW physical address selected
wire  csel;                      // control chip selected
                                 //
reg   qt_req;                    // Q-bus timer request
reg   [5:0] qt_cnt;              // Q-bus timer counter
                                 //
reg   bus_init;                  // Q-bus init signal
reg   init, dclo, pdclo;         // DCLO logic registers
reg   aclo, aclo0;               //
reg   evnt, evnt0;               //
reg   ctl_err;                   //
                                 //
wire  ena_odt;                   // ODT adress translation
reg   [17:16] odt_a;             //
                                 //
wire  aclo_clr, evnt_clr;        //
wire  odt_stb, srun;             //
                                 //
wire  [12:0] svc;                //
wire  [15:0] fdin;               //
reg   [12:4] svc_reg;            //
                                 //
//______________________________________________________________________________
//
// Clock generator
//
always @(posedge vm_clk_p)
begin
   if (mce_p) mclk <= 1'b1;
   if (mce_n) mclk <= 1'b0;
end

assign mce_n = ~init & mclk & qrdy & (drdy | mr_sel & wb_bcyc);
assign mce_p = init | ~mclk & ~mme0;

always @(posedge vm_clk_p)
begin
   mme0 <= ~mc[15] & mce_n & ~init;
   mme1 <= mme0 & ~init;

   if (mce_p)
      qrdy <= mo[12] | (wb_wcnt == 6'o00) | ~vm_clk_slow;
   else
      qrdy <= mc_res | ~wb_swait;

   if (mce_p)
      drdy <= ~(mo[12] & ~(mo[9] & mo[8]) & (wb_bcyc | mo[3] | mo[13]));
   else
      drdy <= qt_req | mc_res | wb_wdone | wb_rdone | wb_idone | mclk & ~mc[12];
end

//______________________________________________________________________________
//
// Initialization curcuits
//
always @(posedge vm_clk_p)
begin
   init <= vm_dclo;
   if (init)
      bus_init <= 1'b0;
   else
      if (mce_n)
         bus_init <= mc[14];
end
assign vm_init = bus_init;

//______________________________________________________________________________
//
// fdin[15:9]   - start address 15:9 (lower bits are zero)
// fdin[8] / E8 set - 1 - start from 173000
//           E8 mis - 0 - start from fdin[15:9]
// fdin[2] / E7 set - 1 - HALT causes exception 10
//           E7 mis - 0 - HALT invokes ODT
//
assign ad = (doe & mclk) ? ad_cpu :
             wdat_oe     ? wb_dat :
             fdin_oe     ? fdin   : ad_mmu;

assign fdin = {wb_dat[15:8], ~init, 4'b0000, wb_dat[2], ~vm_bsel[1:0]};
assign svc = {svc_reg[12:5], ctl_err, ~abort, 1'b0, svc_reg[4], ~dclo };

always @(posedge vm_clk_p or posedge init)
begin
   if (init)
   begin
      wdat_oe <= 1'b0;
      fdin_oe <= 1'b0;
   end
   else
   begin
      wdat_oe <= mclk & ~mce_n & ~mc[3] & (wb_idone | wb_rdone) & ~mr_sel;
      fdin_oe <= mclk & ~mce_n &  mc[3] & wb_idone;
   end
end

always @(posedge vm_clk_p or posedge init)
begin
   if (init)
      dclo <= 1'b1;
   else
      if (mce_p)
         dclo <= pdclo;
end

assign adr_stb = mce_n & ~mc[12] & mc[15] | mme0;
assign ena_odt = mce_n & ~mc[12] & ~m[6] & m[7];

assign bsio =   ena_odt & odt_a[16] & odt_a[17] & bs_cpu
             | ~ena_odt & ((mme0 | mme1) ? bs_mmu : bs_cpu);

always @(posedge vm_clk_p)
begin
   if (odt_stb)
      odt_a[17:16] <= ad_cpu[1:0];
end

//______________________________________________________________________________
//
always @(posedge vm_clk_p)
begin
   if (init)
   begin
      ctl_err <= 1'b0;
      pdclo   <= 1'b1;
   end
   else
      if (mce_n)
      begin
         ctl_err <= ~csel;
         pdclo   <= 1'b0;
      end
end

always @(posedge vm_clk_p or posedge init)
begin
   if (init)
      mc_res <= 1'b1;
   else
   begin
      if (mce_p)
         mc_res <= 1'b0;
      else
         if (abort & ~mclk)
            mc_res <= 1'b1;
         else        // (ctl_err | bus_err)
            if (mce_n & (~csel | qt_req | dclo))
               mc_res <= 1'b1;
   end
end

//______________________________________________________________________________
//
// Interrupts
//
assign srun     = (mc[3:0] == 4'b0001) & mce_n;
assign evnt_clr = (mc[3:0] == 4'b0101) & mce_n;
assign aclo_clr = (mc[3:0] == 4'b0110) & mce_n;
assign odt_stb  = (mc[3:0] == 4'b0111) & mce_n;

always @(posedge vm_clk_p)
begin
   aclo0 <= vm_aclo;
   evnt0 <= vm_evnt;
end

always @(posedge vm_clk_p or posedge init)
begin
   if (init)
      aclo <= 1'b0;
   else
      if (aclo_clr)
         aclo <= 1'b0;
      else
         if (~aclo0 & vm_aclo)
            aclo <= 1'b1;
end

always @(posedge vm_clk_p or posedge bus_init)
begin
   if (bus_init)
      evnt <= 1'b0;
   else
      if (evnt_clr)
         evnt <= 1'b0;
      else
         if (~evnt0 & vm_evnt)
            evnt <= 1'b1;
end

always @(posedge vm_clk_p)
begin
   if (mce_n)
   begin
      svc_reg[12] <= evnt;
      svc_reg[11] <= vm_virq[4];
      svc_reg[10] <= vm_virq[5];
      svc_reg[9]  <= vm_virq[6];
      svc_reg[8]  <= vm_virq[7];
      svc_reg[7]  <= aclo;
      svc_reg[6]  <= 1'b1;
      svc_reg[5]  <= vm_halt;
      svc_reg[4]  <= qt_req;
   end
end

//______________________________________________________________________________
//
// Instantiate chipset components
//
dc302 data
(
   .pin_clk(vm_clk_p),
   .pin_mce_p(mce_p),
   .pin_mce_n(mce_n),
   .pin_ado(ad_cpu),
   .pin_adi(ad),
   .pin_m(m),
   .pin_mo(mo[14:0]),
   .pin_mc(mc),
   .pin_bsi(bsio),
   .pin_bso(bs_cpu),
   .pin_aden(doe),
   .pin_pga(pga),
   .pin_psw(io_psw)
);

defparam ctl.DC303_FPP = F11_CORE_FPP;
dc303 ctl
(
   .pin_clk(vm_clk_p),
   .pin_mce_p(mce_p),
   .pin_mce_n(mce_n),
   .pin_ad(ad),
   .pin_m(m),
   .pin_mc(mc),
   .pin_svc(svc),
   .pin_bra(mo[11]),
   .pin_rst(mc_res),
   .pin_cs(csel)
);

defparam mmu.DC304_FPP = F11_CORE_FPP;
defparam mmu.DC304_MMU = F11_CORE_MMU;
dc304 mmu
(
   .pin_clk(vm_clk_p),
   .pin_mce_p(mce_p),
   .pin_mce_n(mce_n),
   .pin_ado(ad_mmu),
   .pin_adi(ad),
   .pin_a(at_mmu),
   .pin_m(m[12:4]),
   .pin_mo(mo[12:4]),
   .pin_mc(mc[12:4]),
   .pin_m15(mo[15]),
   .pin_me(umap),
   .pin_ra(mr_sel),
   .pin_de(abort),
   .pin_bsi(bsio),
   .pin_bso(bs_mmu),
   .pin_pga(pga)
);

always @(posedge vm_clk_p) if (mce_p) mc <= mo;

//______________________________________________________________________________
//
// Bus transaction timer
//
always @(posedge vm_clk_p or posedge init)
begin
   if (init)
   begin
      qt_cnt <= 6'b000000;
      qt_req <= 1'b0;
   end
   else
      if ((wbm_stb_o & wbm_gnt_i) | wbi_stb_o)
      begin
         qt_cnt <= qt_cnt + 6'b000001;
         qt_req <= &qt_cnt | qt_req;
      end
      else
      begin
         qt_cnt <= 6'b000000;
         if (mce_p)
            qt_req <= 1'b0;
      end
end

//______________________________________________________________________________
//
// Wishbone master and interrupt interfaces
//
assign vm_umap    = umap;
assign io_wtbt    = mce_p & wb_bcyc & mo[12] & ~mo[9] & ~mo[8];
assign iow_req    = mce_p & wb_bcyc & mo[12] & ~mo[9];
assign ior_req    = mce_p & wb_bcyc & mo[12] & mo[9] & ~mo[8];

assign wb_wdone   = wb_stb & wbm_ack_i & wb_we;
assign wb_rdone   = wb_stb & wbm_ack_i & ~wb_we;
assign wb_idone   = (wb_iako | wb_fdin) & wbi_ack_i;

assign wbm_ios_o  = wb_ios;
assign wbm_adr_o  = wb_adr;
assign wbm_dat_o  = ad_cpu;
assign wbm_cyc_o  = wb_bcyc;
assign wbm_we_o   = wb_we & ~mr_sel;
assign wbm_stb_o  = ~wb_iako & wb_stb & ~mr_sel;
assign wbm_sel_o  = wb_sel;
assign wbi_stb_o  = wb_iako | wb_fdin;
assign wbi_una_o  = wb_fdin;

assign wb_wset    = mce_p & ~mo[12] & vm_clk_slow & (wb_wcnt != 6'o00);
assign wb_wclr    = mc_res | ~vm_clk_slow | (vm_clk_ena & (wb_wcnt == 6'o01));

//
// Interrupt Wishbone is simplified and has no clock moderator
//
always @(posedge vm_clk_p or posedge mc_res)
begin
   if (mc_res)
   begin
      wb_iako <= 1'b0;
      wb_fdin <= 1'b0;
   end
   else
   begin
      if (mce_p)
      begin
         if (mo[13])
            wb_iako <= 1'b1;
         if (mo[3])
            wb_fdin <= 1'b1;
      end
      else
         if (wb_idone)
         begin
            wb_iako <= 1'b0;
            wb_fdin <= 1'b0;
         end
   end
end

//
// Wishbone Master state machine
//
always @(posedge vm_clk_p or posedge mc_res)
begin
   if (mc_res)
   begin
      //
      // Master wishbone abort/reset
      //
      wb_swait <= 1'b0;
      wb_wcnt <= 6'o00;
      wb_bcyc <= 1'b0;
      wb_we   <= 1'b0;
      wb_sel  <= 2'b00;
      wb_stb  <= 1'b0;
   end
   else
   begin
      //
      // Clock moderator delays the I/O transaction
      // beginning till counter elapsed
      //
      if (wb_wclr)
         wb_swait <= 1'b0;
      else
         if (wb_wset)
            wb_swait <= 1'b1;

      if (wb_swait)
      begin
         if ((vm_clk_ena | ~vm_clk_slow) & (wb_wcnt != 6'o00))
            wb_wcnt <= wb_wcnt - 6'o01;
      end
      else
      begin
         if (~vm_clk_ena & vm_clk_slow & (wb_wcnt != 6'o77))
            wb_wcnt <= wb_wcnt + 6'o01;
      end
      //
      // Strobe bus address and I/O bank select
      //
      if (adr_stb)
      begin
         wb_adr[15:0]  <= mme0 ? ad_mmu : ad_cpu;
         wb_adr[21:16] <= mme0 ? at_mmu[21:16] :
                          ena_odt ? {4'o00, odt_a[17:16]} : 6'o00;
         wb_ios <= bsio;
      end
      //
      // Strobe bus data receiving registers
      //
      if (wb_idone | wb_rdone)
            wb_dat <= wb_idone ? wbi_dat_i : wbm_dat_i;
      //
      // Wishbone cycle start-end
      //
      if (adr_stb)
      begin
         wb_bcyc <= ~mc[7];
         wb_sel  <= mc[7] ? 2'b00 : 2'b11;
         wb_we   <= 1'b0;
         wb_stb  <= 1'b0;
      end
      else
      begin
         if (wb_wdone | wb_rdone | wb_stb & mr_sel | io_psw)
         begin
            if (mc[7])
               wb_bcyc <= 1'b0;
            wb_sel  <= 2'b00;
            wb_we   <= 1'b0;
            wb_stb  <= 1'b0;
         end
         if (ior_req)
         begin
            wb_sel  <= 2'b11;
            wb_we   <= 1'b0;
            wb_stb  <= 1'b1;
         end
         else
         if (iow_req)
         begin
            wb_sel[0] <= ~io_wtbt | ~wb_adr[0];
            wb_sel[1] <= ~io_wtbt |  wb_adr[0];
            wb_we   <= 1'b1;
            wb_stb  <= 1'b1;
         end
      end
   end
end

//_____________________________________________________________________________
//
endmodule
