//
// Copyright (c) 2014-2022 by 1801BM1@gmail.com
//
// DC304 MMU and Register Chip model
//_____________________________________________________________________________
//
module dc304
#(
   parameter DC304_FPP = 1,
   parameter DC304_MMU = 1
)
(
   input          pin_clk,    // main clock
   input          pin_mce_p,  // master rising edge
   input          pin_mce_n,  // master falling edge
   input  [15:0]  pin_adi,    // input address/data bus
   output [15:0]  pin_ado,    // output address/data bus
   output [21:16] pin_a,      // high address bus
   output         pin_bso,    // bus select output
   input          pin_bsi,    // bus select input
   input  [12:4]  pin_m,      // microinstruction bus
   input  [12:4]  pin_mo,     // microinstruction early status
   input  [12:4]  pin_mc,     // microinstruction latched status
   output         pin_m15,    // address conversion flag
   output         pin_me_n,   // mapping enabled
   output         pin_ra_n,   // register access reply
   output         pin_de_n,   // invalid memory access
   input  [2:0]   pin_pga     // early page address
);

//______________________________________________________________________________
//
wire [12:4] m;                // microinstuction register
wire [12:4] mo;               // microinstruction early status
wire [12:4] mc;               // microinstruction latched status
reg  [15:0] va;               // virtual address input register
wire [15:0] d;                // data bus multiplexer
wire [12:1] ax;               // input address multiplexer
wire [21:16] a;               // high address lines
                              //
wire wl, wh, wf;              // write strobe to MMU/FPP registers
wire astb_rc;                 // Q-bus address microcycle
reg  astb;                    // latched address strobe
reg  atreq;                   // address translation request
                              //
reg  sa_7572;                 // SR0 address decoded on A/D bus
reg  sa_7576;                 // SR2 address decoded on A/D bus
reg  sa_2516;                 // SR3 address decoded on A/D bus
reg  sa_2376;                 // PAR/PDR access from register file
reg  sa_23xx;                 // kernel PAR/PDR access
reg  mr_sel;                  // MMU register access on A/D bus
wire fp_sel;                  // floating point register access
                              //
wire sa_7572_rc;              //
wire sa_7574_rc;              //
wire sa_7576_rc;              //
wire sa_2516_rc;              //
wire sa_23xx_rc;              //
wire sa_76xx_rc;              //
wire mr_sel_rc;               //
                              //
reg  [5:0] ba;                // latched bus access address VA/PA
reg  [4:0] lb;                //
wire [4:0] ma;                // MMU register index port PAR
wire [4:0] mb;                // MMU register index port PDR
wire [3:0] id;                //
                              //
wire fa_m765;                 // FPP register index m[7:5]
wire fa_s210;                 // FPP register index ir[2:0] - fsrc/fdst
wire fa_s076;                 // FPP register index ir[7:6] - ac
wire [5:0] fa;                // FPP register index
                              //
wire en_um;                   // UMAP translate
wire en_as;                   // AS, 22-bit address
wire mmu_en;                  // MMU enable
wire bso, bs;                 //
reg  bsi;                     //
                              //
reg [7:0] fa_mux;             //
wire fa_stb;                  //
reg  fa0t;                    //
wire km_sel;                  //
wire km_sel_rc;               //
reg  sr_rst;                  // status register reset
wire w_set, w_clr;            // W (dirty page) flag set/clr
                              //
reg  [15:0] pa;               // page address to translate
reg  [15:0] pd;               // page descriptor to translate
                              //
wire [15:0] rda;              // RAM read data port A
wire [15:0] rdb;              // RAM read data port B
wire [15:0] wda;              // RAM write data port A
wire [15:0] wdb;              // RAM write data port B
wire [15:0] dx;               //
wire [15:0] df;               //
                              //
wire err_stb;                 //
wire sta_stb;                 //
reg  [15:0] sr0;              //
reg  [15:0] sr2;              //
reg  [15:0] sr3;              //
reg  sr2_rw;                  //
                              //
wire ta_err;                  // address translation error
wire req_ro;                  // aborted on readonly
wire req_le;                  // aborted on limit exceeded
wire req_nr;                  // aborted on non-resident
reg  abt;                     //
                              //
wire fr_ena;                  //
wire rx_ena;                  //
                              //
wire [21:6] sa;               // physical address adder
wire lim_err;                 // address comparator result
                              //
//______________________________________________________________________________
//
assign mo[12:4] = pin_mo[12:4];
assign mc[12:4] = pin_mc[12:4];
assign m[12:4] = pin_m[12:4];

always @(posedge pin_clk)
begin
   if (pin_mce_n) va <= pin_adi;
   if (pin_mce_n) bsi <= pin_bsi;
end

assign pin_ado[15:0] = d[15:0];
assign pin_a[21:16] = a[21:16];

assign pin_m15  = DC304_MMU ? ~(astb_rc & mmu_en & ~m[7]) : 1'b1;
assign pin_me_n = DC304_MMU ? ~en_um : 1'b1;
assign pin_ra_n = DC304_MMU ? ~(mr_sel & mc[12] & (~mc[8] | ~mc[9])) : 1'b1;
assign pin_de_n = DC304_MMU ? ~ta_err : 1'b1;
assign pin_bso  = DC304_MMU ? bso : 1'b0;

//______________________________________________________________________________
//
// Internal bus multiplexer
//
assign d[15:0] = ((DC304_MMU && sa_7572) ? (sr0[15:0] & 16'o160157) : 16'o000000)
               | ((DC304_MMU && sa_7576) ? (sr2[15:0] & 16'o177777) : 16'o000000)
               | ((DC304_MMU && sa_2516) ? (sr3[15:0] & 16'o000060) : 16'o000000)
               | ((DC304_MMU && atreq)   ? {sa[15:6], ba[5:0]} : 16'o000000)
               | ((DC304_MMU && rx_ena)  ? dx : 16'o000000)
               | ((DC304_FPP && fr_ena)  ? df : 16'o000000);

//______________________________________________________________________________
//
assign astb_rc = ~mo[12] & m[12];
always @(posedge pin_clk)
begin
   if (pin_mce_p)
   begin
      astb  <= astb_rc;
      atreq <= astb_rc & mmu_en & ~m[7];
   end
end

assign id[0] = mc[12] & ~mc[9];                    // write data
assign id[1] = mc[12] &  mc[9] & ~mc[8];           // read data
assign id[2] = mc[12] & ~mc[9] & ~mc[8] & ~ba[0]   // write low byte
             | mc[12] & ~mc[9] &  mc[8];           // write word
assign id[3] = mc[12] & ~mc[9] & ~mc[8] &  ba[0]   // write high byte
             | mc[12] & ~mc[9] &  mc[8];           // write word
                                                   //
assign wl = pin_mce_n & mr_sel & id[2];            // write MMU low byte
assign wh = pin_mce_n & mr_sel & id[3];            // write MMU high byte
assign wf = pin_mce_n & fp_sel & mc[12] & ~mc[9];  // write FPP word

assign km_sel_rc = ~mo[4] | ~mo[5] | mo[6];                       // early flags
assign km_sel = ~mc[4] | ~mc[5] | mc[6];                          // kernel mode
assign fp_sel = mc[5] & mc[4] & mc[7] & (m[5] | m[6]) & ~mr_sel;  // FPP access

always @(posedge pin_clk)
begin
   if (pin_mce_n)
      sr_rst <= ~mc[4] & ~mc[5] & mc[6] & m[7] & ~mc[9];
end

//______________________________________________________________________________
//
// MMU resister access from the bus transactions (read/write by PDP-11 CPU)
//
assign ax[5:1]  = ba[5:1];
assign ax[12:6] = atreq ? sa[12:6] : va[12:6]; // translated or virtual
assign bs = DC304_MMU  ? (atreq ? bso : bsi) : 1'b0;

assign sa_7572_rc = bs & (ax[12:1] == (13'o17572 >> 1)); // SR0 access
assign sa_7574_rc = bs & (ax[12:1] == (13'o17574 >> 1)); // SR1 access
assign sa_7576_rc = bs & (ax[12:1] == (13'o17576 >> 1)); // SR2 access
assign sa_2516_rc = bs & (ax[12:1] == (13'o12516 >> 1)); // SR3 access
assign sa_23xx_rc = bs & (ax[12:6] == 7'o123) & ~ax[4];  // kernel set
assign sa_76xx_rc = bs & (ax[12:6] == 7'o176) & ~ax[4];  // user set
assign mr_sel_rc  = sa_7572_rc | sa_7574_rc | sa_7576_rc
                  | sa_2516_rc | sa_23xx_rc | sa_76xx_rc;

always @(posedge pin_clk)
begin
   if (pin_mce_n & astb)
      ba[5:0] <= pin_adi[5:0];

   if (pin_mce_p)
   begin
      if (mc[7]) // no SYNC asserted
      begin
         sa_7572 <= 1'b0;
         sa_7576 <= 1'b0;
         sa_2516 <= 1'b0;
         sa_2376 <= 1'b0;
         sa_23xx <= 1'b0;
         mr_sel  <= 1'b0;
      end
      else
      begin
         if (astb)
         begin
            sa_7572 <= sa_7572_rc;
            sa_7576 <= sa_7576_rc;
            sa_2516 <= sa_2516_rc;
            sa_23xx <= sa_23xx_rc;
            sa_2376 <= sa_23xx_rc | sa_76xx_rc;
            mr_sel  <= mr_sel_rc;
         end
      end
   end
end

assign bso = (~en_as | sa[21:18] == 4'b1111) & (sa[17:13] == 5'b11111);
assign fr_ena = ~astb & ~mr_sel;
assign rx_ena = ~astb & sa_2376;

//______________________________________________________________________________
//
// Floating point register indices
//
assign fa_m765 = ~m[7] | ~m[6];        // FPP register in m[7:5]
assign fa_s210 = m[7] & m[6] & ~m[5];  // FPP register in ir[2:0] - fsrc/fdst
assign fa_s076 = m[7] & m[6] & m[5];   // FPP register in ir[7:6] - ac
assign fa_stb  = mc[4] & ~mc[5] & ~mc[6];

always @(posedge pin_clk)
begin
   if (pin_mce_n)
      if (fa_stb)
      begin
         fa_mux[7:0] <= pin_adi[7:0];
         fa0t <= 1'b0;
      end
      else
         if (fp_sel & ~fa[4] & (id[0] | id[1]))
            fa0t <= ~fa0t;
end

assign fa[0]   = fa0t & ~fa[4];
assign fa[5:4] = {~m[4], ~m[6]};
assign fa[3:1] = ( (fa_s210 ? fa_mux[2:0]         : 3'b000)
                 | (fa_m765 ? {~m[7], m[6:5]}     : 3'b000)
                 | (fa_s076 ? {1'b0, fa_mux[7:6]} : 3'b000))
                 & (fa[4] ? 3'b101 : 3'b111);

//______________________________________________________________________________
//
// FPP register file
//
dc_fpp fpr(
   .clock(pin_clk),
   .address(fa),
   .data(pin_adi),
   .wren(wf),
   .rden(pin_mce_p),
   .q(df)
);

//______________________________________________________________________________
//
// MMU error logic
//
assign sta_stb = pin_mce_p & ~abt & atreq;
assign err_stb = pin_mce_p & ~abt & atreq & ta_err;

assign req_nr = atreq & ~pd[1];
assign req_ro = atreq & pd[1] & ~pd[2] & ~mc[9];
assign req_le = atreq & lim_err;
assign ta_err = req_ro | req_le | req_nr;

//______________________________________________________________________________
//
// SRx MMU registers write logic
//
always @(posedge pin_clk)
begin
   if (pin_mce_p)                         // SR0 error bits are frozen
      abt <= sr0[15:13] != 3'b000;        // on the first error occurrence
                                          //
   if (pin_mce_n & astb & m[5] & ~abt)    // instruction fetch address
      sr2 <= pin_adi;                     // frozen on error occurrence
end

always @(posedge pin_clk or posedge sr_rst)
begin
   if (sr_rst)
   begin
      sr0[15:0] <= 16'o000000;
      sr3[15:0] <= 16'o000000;
   end
   else
   begin                                  //
      sr0[4]    <= 1'b0;                  // unused bits of SR0/SR3
      sr0[12:7] <= 6'o00;                 //
      sr3[3:0]  <= 4'o00;                 //
      sr3[15:6] <= 10'o0000;              //
                                          //
      if (wl & sa_2516)                   //
         sr3[5:4] <= pin_adi[5:4];        // SR3 write
                                          //
      if (wl & sa_7572)                   //
         sr0[0] <= pin_adi[0];            // SR0 write
                                          //
      if (wh & sa_7572)                   //
         sr0[15:13] <= pin_adi[15:13];    //
      else
         if (err_stb)
            sr0[15:13] <= {req_nr, req_le, req_ro};

      if (sta_stb)
      begin
         sr0[3:1] <= va[15:13];
         sr0[6:5] <= {~km_sel, ~km_sel};
      end
   end
end

assign mmu_en = sr0[0];
assign en_as  = sr3[4];
assign en_um  = sr3[5];

//______________________________________________________________________________
//
// MMU register file indices
//
always @(posedge pin_clk) if (pin_mce_p) lb <= mb;

assign ma = astb_rc ? {1'b1, km_sel_rc, pin_pga[2:0]} :  // translation PAR
            astb ?    {ba[5], sa_23xx_rc, ba[3:1]} :     // read/write RxR
                      {ba[5], sa_23xx, ba[3:1]};
assign mb = astb_rc ? {1'b0, km_sel_rc, pin_pga[2:0]} :  // translation PDR
            astb ? lb : {1'b0, sa_23xx, ba[3:1]};        // W-bit clr/set

//______________________________________________________________________________
//
// MMU register file
//
dc_mmu mrx(
   .clock(pin_clk),
   .address_a(ma),
   .address_b(mb),
   .byteena_a({wh, wl}),
   .byteena_b(2'b01),
   .data_a(wda),
   .data_b(wdb),
   .q_a(rda),
   .q_b(rdb),
   .wren_a(rx_ena & (wl | wh)),
   .wren_b(pin_mce_p & w_set | pin_mce_n & w_clr)
);

always @(posedge pin_clk)
begin
   if (pin_mce_n & atreq) pa <= rda;
   if (pin_mce_n & (atreq | sa_2376)) pd <= rdb & 16'o077516;
end

assign dx  = rda & (ba[5] ? 16'o177777 : 16'o077516);
assign wda = pin_adi[15:0] & (~ba[5] ? 16'o077416 : 16'o177777);
assign wdb = pd & 16'o077416 | (w_set ? 16'o000100 : 16'o000000);

assign w_set = ~mr_sel_rc & ~mc[9] & ~ta_err & atreq;    // write not MMU reg
assign w_clr = sa_2376 & ba[5] & ~mc[9] & mc[12];        // write PAR

//______________________________________________________________________________
//
// Address adder and comparator
//
assign lim_err = ~pd[3] ? (va[12:6] > pd[14:8]) :     // page grows up
                          (va[12:6] < pd[14:8]);      // page grows down

assign a[21:16] = (DC304_MMU && atreq) ? sa[21:16] : 6'b000000;
assign sa[21:6] = {(en_as ? pa[15:12] : 4'b0000), pa[11:0]} + {9'o000, va[12:6]};

//______________________________________________________________________________
//
endmodule
