Câu đố logic


8

Các thách thức

Đơn giản chỉ cần viết một chương trình xuất mã nguồn riêng của nó.

Không có gì hơn một quine thông thường.

Vấn đề

Chúng tôi không có máy tính nên chúng tôi phải chạy chương trình trên một thiết bị logic có thể lập trình (chẳng hạn như một đồ họa, CPLD, mảng cổng ...).

Những quy định

  • Bất kỳ thiết bị nào có sẵn trên thị trường (chẳng hạn như máy in được kết nối qua cổng Centrics, màn hình LED, thiết bị đầu cuối RS232 ...) được kết nối với thiết bị logic đều có thể được sử dụng để xuất chương trình.
  • Nếu bạn sử dụng bất kỳ loại thiết bị lập trình nào làm thiết bị đầu ra, bạn không được phép đặt bất kỳ logic chương trình nào ở đó!

    Ví dụ: Nếu bạn sử dụng RS232 để gửi dữ liệu tới máy tính, máy tính không phải làm gì ngoài việc hiển thị dữ liệu nhận được từ RS232. Tuy nhiên, bạn được phép bật các tùy chọn RS232 như lặp lại dữ liệu cho thiết bị logic nếu bất kỳ chương trình thiết bị đầu cuối hiện có nào có tính năng này.

  • Bất kỳ mã hóa "tiêu chuẩn" (gần đây hoặc lịch sử) (ASCII, UNICODE, EBCDIC, mã Morse ...) đều có thể được sử dụng.

  • Chương trình chỉ cần xuất mã nguồn của chính nó. Tệp chỉ chứa ánh xạ giữa các dây "VHDL / Verilog / ..." và các chân I / O thực tế, tệp chứa cài đặt trình biên dịch và các tệp tương tự không được coi là "mã nguồn" và không cần phải ghi.
  • Bạn có một pin đầu vào đồng hồ với tần suất bạn chọn (nếu bạn cần).
  • Không được sử dụng các đơn vị trên chip (như SRAM trên chip hoặc số nhân trên chip).
  • Để kiểm tra mã, bạn có thể mô phỏng thiết bị đầu ra bằng mã bổ sung; tất nhiên bạn cũng có thể mô phỏng thiết bị logic (nếu bạn không có thiết bị thật).
  • Tiêu chuẩn áp dụng.

Người chiến thắng

  • Để tính kích thước của chương trình, giả định rằng thiết bị đầu ra thực (ví dụ: máy in) được kết nối với một số chân I / O của thiết bị logic.
  • Mã yêu cầu ít ô "LE" nhất trên FPGA của tôi (Altera EP2C20F484C7) sẽ thắng.
  • Nếu FPGA của tôi quá nhỏ (= không đủ lớn cho giải pháp nhỏ nhất), chúng tôi sẽ biên dịch cho giải pháp lớn nhất có các ô "LE" (EP4CGX150DF31I7).
  • Nếu cái đó vẫn chưa đủ, chúng tôi sẽ thử cái lớn nhất được hỗ trợ bởi trình biên dịch miễn phí (EP2AGX260FF35I5).
  • Nếu thiết bị đó vẫn còn quá nhỏ thì kích thước của mã nguồn sẽ được tính.

Ghi chú

Tìm kiếm "quine VHDL" trong Google Tôi đã tìm thấy ít nhất ba quines được viết bằng VHDL trên trang đầu tiên!

Thật không may, không ai trong số họ sẽ làm việc trên một thiết bị logic thực mà chỉ trong trình giả lập vì đầu ra tiêu chuẩn (của trình giả lập) được sử dụng.

Chúc may mắn!

Câu trả lời:


6

Verilog, được tối ưu hóa cho khu vực, cổng 130 LE

Bản thân quine (tệp thực tế được mã hóa trong DEC SIXBIT ):

module q(s,d);inout s,d;wire[0:1023]v=1024'b0110111100101001011001111110110110010110100100001111011010010111010101110101010110010110111100101001011001111110110100100110100100001111011010100111010101110110111000011100111100111010011001111011100000001001000111011010100111000101100111111010100111010111010100100110101101101110111010011111010110111000011011001101111000011110011100111000000010001100001011111100111001011001001001111001010000001100110010011010011001100010001010100111100101010010011000101001011001111010011011100000001010010111011010010010110100010110111010100111011010100001100100011111100000011010010111110101110110100100010110111001011011101001000000001001011011001100111001010000001010100111011010100010110100010110111001011011101001001011011011111001001101011011001001010000000000000000101101101111100100110101101100100101000000110001001000110011001100100100001001011011101001101110101111110101110100000000110011001100100100011011110111101001110010100101111011010000011010010001010000010010010011111101110110011101010001010000010010010100000111100010;reg[9:0]i=759;reg[2:0]j=7;assign d=j<6?j==2:v[i];always@(posedge s)if(j>5)begin i=i+1;j=j&1^!i?7:1;end else j=j+1;endmodule

Phiên bản có thể đọc được với các bình luận và một testbench:

module q(s,d);
   // Declare the ports. Making them both "inout" is shortest.
   inout s,d;
   // Data storage for the program.
   wire[0:1023]v=1024'b{DATA GOES HERE};
   // i is the current bit number within the program.
   // This is relative to the /end of the data storage/ (not to the start
   // of the program), so it starts at a nonzero value so that the output
   // starts at the start of the program.
   reg[9:0]i=759;
   // When expanding bits to (6-bit) bytes, j is the bit number within
   // the expansion, from 1 for the first bit up to 6 for the last.
   // When not expanding, j is always 7.
   // DEC SIXBIT encoding for 0 is (from MSB to LSB) 010 000.
   // DEC SIXBIT encoding for 1 is (from MSB to LSB) 010 001.
   // We use SSI encoding for the output, so the MSB is sent first.
   reg[2:0]j=7;
   assign d=j<6?j==2:v[i];
   // When we get a strobe:
   always@(posedge s)
     // If we just output a bit, move onto the next bit.
     // We may also need to reset j.
     if(j>5)
       begin 
          i=i+1;
          j=j&1^!i?7:1;
       end 
     else 
       // If we're inside a bit, continue to output that bit.
       j=j+1;
endmodule
// {TESTBENCH BELOW HERE}

`timescale 10ns / 1ns
module testbench();
   reg clock = 0;
   wire data, strobe;

   always
     #1 clock <= !clock;
   initial
     #14304 $finish;

   assign strobe = clock;
   q testquine(.s(strobe),.d(data));

   always @(negedge strobe)
      $display("%d", data);

endmodule // testbench

Việc sử dụng Verilog mang lại cho tôi quyền kiểm soát đáng kể hơn đối với các chi tiết cấp thấp hơn tôi có với Verity. Đặc biệt, nó cho phép tôi điều khiển đồng hồ và tự đặt lại quy tắc. Chương trình này dành cho kết nối nối tiếp đồng bộ với đầu vào nhấp nháy svà đầu ra dữ liệud. Mặc dù mỗi chỉ được sử dụng theo một hướng, tôi đã khai báo cả hai là hai chiều để lưu một vài byte; Tôi đã phải đánh golf các phần không phải dữ liệu của chương trình xuống còn 1024 bit để có thể sử dụng cổng logic 10 bit bên trong (các bit phụ sẽ đắt hơn trong khu vực) và nó chỉ bị loại bỏ ở mức 1008, vì vậy tiết kiệm như Điều này rất quan trọng. Để tiết kiệm một lượng mã đáng kể, tôi dựa vào mạch thiết lập lại phần cứng của FPGA thay vì thêm mã của riêng tôi, và tôi hợp nhất các đầu vào nhấp nháy và đồng hồ (đây là một thủ thuật cũ mà ngày nay rất khó chịu vì nó khó để giữ cho cây đồng hồ cân bằng ở tốc độ đồng hồ cao, nhưng nó hữu ích cho việc chơi gôn.) Tôi hy vọng điều đó có thể tổng hợp được; Tôi không biết các trình tổng hợp Verilog đối phó với việc sử dụng cổng hai chiều như một chiếc đồng hồ.

Nguồn được mã hóa trong DEC SIXBIT (Tôi giả sử ở đây rằng chúng ta giải thích bảng chữ cái duy nhất của chữ cái là chữ thường; bộ tổng hợp Verilog sẽ không có lý do để sử dụng cách hiểu chữ hoa). Tôi đã sử dụng một ký tự sáu bit được đặt bên trong trong giải pháp khác của mình, sau đó lãng phí byte chuyển đổi nó; tốt hơn là sử dụng một bộ ký tự rộng sáu bit "tự nhiên" để không cần chuyển đổi. Tôi đã chọn bộ ký tự sáu bit đặc biệt này bởi vì 01chỉ khác nhau ở bit có ý nghĩa nhỏ nhất của chúng và chỉ có một bộ bit khác, nghĩa là mạch chuyển đổi một chữ số nhị phân thành DEC SIXBIT (nghĩa là "thoát" một chuỗi) có thể rất đơn giản. Thật thú vị, bộ ký tự trong câu hỏi đang thiếu một ký tự dòng mới; tất cả các chương trình ban đầu trên một dòng không chỉđể làm cho nó dễ dàng hơn để tạo ra, nhưng để có thể mã hóa! Thật là một điều tốt khi Verilog chủ yếu không quan tâm đến khoảng trắng.

Giao thức gửi dữ liệu đến máy chủ dựa trên Giao diện nối tiếp đồng bộ. Tôi đã chọn nó vì nó có đồng hồ (cho phép tôi sử dụng thủ thuật đồng hồ / nhấp nháy và cũng cho phép tôi viết chương trình di động không dựa trên các thiết bị đo thời gian trên chip) và vì nó rất đơn giản (vì vậy tôi không phải lãng phí nhiều mã thực hiện nó). Giao thức này không chỉ định phương thức xác định nơi thông báo kết thúc (máy chủ được cho là biết); trong trường hợp cụ thể này, tôi đã đệm đầu ra lên tới bội số 1024 bit với 0 bit (tổng cộng 16 bit đệm), sau đó (theo yêu cầu của SSI), thông báo khởi động lại. (Tôi không triển khai bộ hẹn giờ ở chế độ không sử dụng; mục đích của nó là xác định xem có nên gửi tin nhắn mới hay không để lặp lại tin nhắn trước đó và vì chương trình này luôn gửi mã nguồn riêng của mình dưới dạng tin nhắn, sự khác biệt không hiển thị Bạn có thể coi nó là độ dài 0,

Về mặt logic thực tế, điều thú vị nhất là cách tôi chia nhỏ các biến để giảm lượng diện tích cần thiết trên chip. i, thanh ghi lớn hơn, giữ "địa chỉ" hiện tại trong dữ liệu của chương trình và chỉ được thay đổi thông qua việc tăng nó; điều này có nghĩa là logic của nó có thể được tổng hợp bằng cách sử dụng cấu trúc nửa bộ cộng (như tên gọi của nó, chỉ sử dụng một nửa tài nguyên mà bộ cộng thực hiện; điều này chủ yếu chỉ quan trọng trên các GPU nhỏ nhất, những cái lớn hơn sẽ sử dụng 3 đầu vào hoặc thậm chí Các LUT 4 đầu vào đủ mạnh để chúng có nhiều năng lực lãng phí khi tổng hợp một nửa bộ cộng). Sổ đăng ký nhỏ hơn,j, về cơ bản là trạng thái máy trạng thái và do đó xử lý hầu hết logic phức tạp của chương trình. Nó đủ nhỏ để nó có thể được xử lý hoàn toàn thông qua bảng tra cứu trên một đồ họa lớn hơn (làm cho logic cơ bản biến mất); trong trường hợp chương trình được tổng hợp cho một FPGA nhỏ, tôi đã chọn mã hóa của nó theo cách mà ít phần mã quan tâm đến cả ba bit của nó cùng một lúc.

Cũng đáng lưu ý rằng tôi đã cho phép lưu trữ dữ liệu theo chu kỳ. Chúng ta có thể bắt đầu ichỉ bất cứ nơi nào bên trong nó, không nhất thiết phải bắt đầu. Với sự sắp xếp được thấy ở đây, chúng ta có thể in itrực tiếp từ giá trị ban đầu đến cuối, sau đó in toàn bộ mảng đã thoát, sau đó in từ đầu đến giá trị ban đầu của i, để in tất cả các phần của dữ liệu trong đúng nơi mà không cần phải lưu và khôi phục giá trị của i. (Thủ thuật này có thể hữu ích cho các câu hỏi trong các ngôn ngữ khác.)

Nguồn dài 1192 byte 6 bit, tương đương 894 byte 8 bit. Thật là xấu hổ khi điều này chứa ít byte nguồn hơn trình Verity của tôi, mặc dù được tối ưu hóa cho một thứ hoàn toàn khác; điều này chủ yếu là do Verilog có chuỗi và Verity không có nghĩa là mặc dù tôi đã mã hóa chương trình theo dạng nhị phân chứ không phải bát phân (về cơ bản là kém hiệu quả hơn về kích thước mã nguồn), tôi có thể mã hóa từng byte của chương trình sử dụng sáu ký tự sáu bit (một cho mỗi bit) thay vì tám ký tự tám bit (bốn cho mỗi chữ số bát phân). Một bản đệ trình Verilog mã hóa chương trình theo bát phân có thể sẽ nhỏ hơn về kích thước mã nguồn, nhưng gần như chắc chắn sẽ có diện tích lớn hơn.

Tôi không biết chương trình này sẽ sử dụng bao nhiêu khu vực; nó phụ thuộc rất nhiều vào khả năng của trình tối ưu hóa trong bộ tổng hợp Verilog của bạn (bởi vì vấn đề tối thiểu hóa việc chuyển đổi dữ liệu được lưu trữ thành một bộ cổng logic là điều được thực hiện trong chính trình tổng hợp; ném công việc vào bộ tổng hợp làm cho chính mã nguồn ngắn hơn nhiều, và do đó làm giảm diện tích cần thiết để lưu trữ nó). Tuy nhiên, nó phải có độ phức tạp là O (n log n) và do đó nhỏ hơn nhiều so với O (n²) của chương trình khác. Tôi rất muốn thấy OP cố gắng chạy nó trên đồ họa của họ. (Tuy nhiên, có thể mất khá nhiều thời gian để tổng hợp; tuy nhiên, có nhiều bước khác nhau bạn có thể thực hiện để tối ưu hóa chương trình trong thời gian biên dịch nhưng tôi đã không thực hiện bất kỳ điều gì ở đây vì nó sẽ gây ra một chương trình lớn hơn = diện tích lớn hơn.)


1
Trình biên dịch đầu tiên chạy nói rằng chỉ có 130 cổng LE được sử dụng. Vì một cổng LE chỉ có thể lưu trữ 2 bit dữ liệu và bạn sử dụng luồng dữ liệu ~ 750 bit, điều này có nghĩa là trình biên dịch đã nén dữ liệu hoặc có lỗi xảy ra trong khi biên dịch. Ngày mai tôi sẽ xác minh xem kết quả của trình biên dịch có đúng không.
Martin Rosenau

Với cấu trúc này, phần lớn dữ liệu được lưu trữ theo mô hình kết nối giữa các cổng chứ không phải chính các cổng, vì vậy tôi có thể tin rằng 130 cổng LE là chính xác. (Trình tổng hợp về cơ bản là brute - buộc một công thức ánh xạ chỉ mục 10 bit thành giá trị 1 bit; Tôi đã chỉ định công thức trong chương trình Verilog bằng bảng tra cứu với 1024 mục, nhưng trình tổng hợp rất có thể sử dụng nhiều hơn biểu diễn hiệu quả dựa trên thứ gì đó như thu nhỏ bản đồ K.)

Ồ, có lẽ bạn cũng nên kiểm tra để đảm bảo rằng trình biên dịch chưa tối ưu hóa một phần mã vào khối RAM hoặc khối ROM (không được phép bởi câu hỏi). Tôi đã không yêu cầu một, và đã không viết mã dưới dạng có nghĩa là một (tôi cẩn thận thực hiện kết hợp bảng tra cứu), nhưng đôi khi tối ưu hóa trình biên dịch làm những điều kỳ lạ. Nếu có một tối ưu hóa can thiệp vào điều đó, bạn sẽ phải tắt tối ưu hóa.

1
Đồng ý. Tôi quản lý các vấn đề với các chân bây giờ. Mã dường như hoạt động tốt. Nổi bật. Chỉ có 130 tế bào loại LE! RAM, ROM, vv không được sử dụng. Tôi nghĩ trình biên dịch thực hiện một số tối ưu hóa tương tự như sơ đồ KV để "nén" dữ liệu.
Martin Rosenau

1
Bạn thắng! Xin chúc mừng.
Martin Rosenau

3

Độ chính xác 0.10, được tối ưu hóa cho kích thước mã nguồn (1944 byte)

Ban đầu tôi đã đọc sai câu hỏi và giải thích nó như một . Đó có lẽ là điều tốt nhất, vì việc viết một câu hỏi với mã nguồn ngắn dễ dàng hơn nhiều so với mã đối tượng ngắn theo các hạn chế trong câu hỏi; điều đó làm cho câu hỏi đủ dễ để tôi cảm thấy mình có thể đưa ra câu trả lời một cách hợp lý và có thể hoạt động như một bước đệm trên đường đến một câu trả lời tốt hơn. Nó cũng nhắc tôi sử dụng ngôn ngữ cấp cao hơn cho đầu vào, nghĩa là tôi cần thể hiện ít hơn trong chính chương trình. Tôi đã không tạo Verity như một ngôn ngữ chơi gôn cho phần cứng (tôi thực sự được thuê để tạo ra nó một thời gian trước trong một bối cảnh hoàn toàn khác), nhưng có một sự hồi tưởng ở đó (ví dụ như nó ở cấp độ cao hơn so với HDL thông thường, và nó có ít nồi hơi hơn, nó cũng dễ mang hơn nhiều so với HDL thông thường).

Tôi khá chắc chắn rằng giải pháp chính xác cho mã đối tượng ngắn liên quan đến việc lưu trữ dữ liệu trong một loại cấu trúc cây nào đó, cho rằng câu hỏi không cho phép sử dụng ROM khối, đó là nơi bạn thường lưu trữ nó trong một chương trình thực tế; Tôi có thể bắt đầu viết một chương trình sử dụng nguyên tắc này (không chắc ngôn ngữ nào, có thể là Verity, có thể là Verilog; VHDL có quá nhiều bản tóm tắt để có thể tối ưu cho loại vấn đề này) vào một lúc nào đó. Điều đó có nghĩa là bạn sẽ không cần phải truyền từng bit mã nguồn cho mỗi bit của "ROM được tạo thủ công" của bạn. Tuy nhiên, trình biên dịch Verity hiện đang tổng hợp cấu trúc của đầu ra dựa trên mức độ ưu tiên và kết hợp của đầu vào, nghĩa là nó đại diện hiệu quả cho con trỏ lệnh (do đó là chỉ mục cho bảng tra cứu) trong unary,

Chương trình chính nó:

import <print>new x:=0$1296in(\p.\z.\a.new y:=(-a 5-a 1-a 1-a 2-a 4-a 2-a 3-a 2-a 6-a 2-a 0-a 3-a 0-a 4-a 4-a 7-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 6-a 7-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 4-a 3-a 2-a 7-a 5-a 7-a 0-a 6-a 4-a 4-a 1-a 6-a 2-a 6-a 1-a 7-a 6-a 6-a 5-a 1-a 2-a 2-a 0-a 5-a 0-a 0-a 4-a 2-a 6-a 5-a 0-a 0-a 6-a 3-a 6-a 5-a 0-a 0-a 5-a 0-a 6-a 5-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 5-a 3-a 2-a 7-a 5-a 7-a 0-a 5-a 5-a 5-a 1-a 4-a 4-a 3-a 1-a 5-a 5-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 4-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 5-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 4-a 7-a 3-a 6-a 2-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 6-a 3-a 3-a 5-a 1-a 7-a 2-a 6-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 6-a 3-a 1-a 5-a 3-a 7-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 5-a 7-a 5-a 7-a 4-a 6-a 5-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 5-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 4-a 1-a 7-a 7-a 6-a 3-a 7-a 4-a 2-a 0-a 4-a 3-a 6-a 2-a 6-a 3-a 7-a 4-a 2-a 0-a 5-a 4-a 6-a 0-a 7-a 2-a 0-a 1-a 4-a 5-a 3-a 4-a 4-a 4-a 4-a 3-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 3-a 7-a 4-a 2-a 0-a 4-a 4-a 6-a 5-a 6-a 3-a 7-a 5-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 5-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 1-a 5-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 7-a 2-a 7-a 1-a 5-a 1-a 4-a 2-a 3-a 7-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 6-a 6-a 1-a 5-a 1-a 5-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 0-a 5-a 1-a 4-a 4-a 3-a 4-a 4-a 4-a 4-a 6-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 0-a 5-a 0-a 0-a 0-a 1-a 6-a 5-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 2-a 0-a 0-a 1-a 4-a 7-a 4-a 7-a 1-a 6-a 2-a 6-a 1-a 7-a 3-a 6-a 3-a 7-a 0-a 6-a 1-a 5-!x)in while!x>0do(p(if z<32then z+92else z);if z==45then while!y>0do(p 97;p 32;p(48^!y$$3$$32);p 45;y:=!y>>3)else skip;x:=!x>>6))print(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)

Dễ đọc hơn:

import <print>
new x := 0$1296 in
(\p.\z.\a.
  new y := (-a 5-a 1-
            # a ton of calls to a() omitted...
            -a 1-a 5-!x) in
  while !x>0 do (
    p(if z<32 then z+92 else z);
    if z==45
    then while !y>0 do (
      p 97;
      p 32;
      p(48^!y$$3$$32);
      p 45;
      y:=!y>>3 )
    else skip;
    x:=!x>>6
  )
)(print)(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)

Ý tưởng cơ bản là chúng tôi lưu trữ toàn bộ dữ liệu trong biến x. (Như thường lệ đối với quine, chúng tôi có phần mã và phần dữ liệu; dữ liệu mã hóa văn bản của mã và cũng có thể được sử dụng để tạo lại văn bản của dữ liệu.) Thật không may, Verity hiện không cho phép rất lớn Các hằng số được ghi trong mã nguồn (nó sử dụng các số nguyên OCaml trong quá trình biên dịch để biểu diễn các số nguyên trong nguồn, điều này rõ ràng không đúng trong ngôn ngữ hỗ trợ các loại số nguyên rộng tùy ý) - và bên cạnh đó, nó không cho phép các hằng số được chỉ định trong bát phân - vì vậy chúng tôi tạo ra giá trị của xthời gian chạy thông qua các cuộc gọi lặp lại cho một hàma. Chúng ta có thể tạo một hàm void và gọi nó liên tục dưới dạng các câu lệnh riêng biệt, nhưng điều đó sẽ khiến việc xác định nơi bắt đầu xuất văn bản của phần dữ liệu trở nên khó khăn. Vì vậy, thay vào đó, tôi đã atrả về một số nguyên và sử dụng số học để lưu trữ dữ liệu (Verity đảm bảo rằng số học đánh giá từ trái sang phải). Phần dữ liệu được mã hóa xbằng một -dấu hiệu duy nhất ; khi điều này gặp phải trong thời gian chạy, nó được mở rộng đến toàn bộ -a 5-a 1-, v.v., thông qua việc sử dụng y.

Khởi tạo ynhư một bản sao xkhá tinh tế ở đây. Bởi vì atrả về 0 một cách cụ thể, hầu hết số tiền chỉ bằng 0 trừ 0 trừ đi và tự hủy. Chúng tôi kết thúc bằng !x(tức là "giá trị của x"; trong Verity, như trong OCaml, tên của một biến hoạt động giống như một con trỏ hơn bất kỳ thứ gì khác và bạn phải xác định rõ ràng để lấy giá trị của biến đó. Các quy tắc của Verity cho phép trừ đơn nguyên là một chút phức tạp - phép trừ đơn nguyên vđược viết là (-v)- do đó (-0-0-0-!x)phân tích thành (-(0-0-0-!x)), bằng !x, và cuối cùng chúng ta khởi tạo ynhư một bản sao của x. (Cũng đáng lưu ý rằng Verity không phảigọi theo giá trị, nhưng thay vào đó cho phép các hàm và toán tử chọn thứ tự họ đánh giá mọi thứ; -sẽ đánh giá đối số bên trái trước đối số bên phải và đặc biệt, nếu đối số bên trái có tác dụng phụ, chúng sẽ hiển thị khi đối số bên phải được đánh giá.)

Mỗi ký tự của mã nguồn được biểu diễn bằng hai chữ số bát phân. Điều này có nghĩa là mã nguồn được giới hạn ở 64 ký tự khác nhau, vì vậy tôi phải tạo một bảng mã riêng để sử dụng nội bộ. Đầu ra là ASCII, vì vậy tôi cần chuyển đổi nội bộ; đây là những gì (if z<32 then z+92 else z)dành cho. Đây là bộ ký tự tôi đã sử dụng trong biểu diễn bên trong, theo thứ tự số (nghĩa là \có mã số 0, ?có mã số 63):

\]^_`abcdefghijklmnopqrstuvwxyz{ !"#$%&'()*+,-./0123456789:;<=>?

Bộ ký tự này cung cấp cho chúng ta hầu hết các nhân vật quan trọng đối với Verity. Các ký tự đáng chú ý bị thiếu là }(có nghĩa là chúng ta không thể tạo một khối bằng cách sử dụng {}, nhưng may mắn là tất cả các câu lệnh đều là biểu thức để chúng ta có thể sử dụng() thay thế); và |(đây là lý do tại sao tôi phải sử dụng độc quyền thay vì bao gồm HOẶC khi tạo giá trị x, nghĩa là tôi cần khởi tạo nó thành 0; tuy nhiên, tôi cần phải xác định mức độ lớn của nó). Một số ký tự quan trọng mà tôi muốn đảm bảo có trong bộ ký tự là <>(đối với nhập, cũng thay đổi), ()(rất khó để viết chương trình có thể được phân tích cú pháp mà không có các ký tự này), $(đối với mọi thứ phải làm với băng thông) và \( đối với lambdas, về mặt lý thuyết chúng ta có thể giải quyết vấn đề này vớilet…in nhưng nó sẽ dài dòng hơn nhiều).

Để làm cho chương trình ngắn hơn một chút, tôi đã viết tắt print!x$$6$$32(ví dụ: "6 bit dưới cùng !x, được sử dụng để printthư viện) thông qua việc liên kết chúng với các đối số lambda.

Cuối cùng, có vấn đề về đầu ra. Verity cung cấp mộtprint thư viện dành cho đầu ra gỡ lỗi. Trên một trình giả lập, nó in mã ASCII thành đầu ra tiêu chuẩn, hoàn toàn có thể sử dụng để kiểm tra chương trình. Trên một bảng mạch vật lý, nó phụ thuộc vào một printthư viện đã được viết cho chip và bảng cụ thể xung quanh nó; có một printthư viện trong bản phân phối Verity cho một bảng đánh giá mà tôi có quyền truy cập để in đầu ra trên màn hình bảy đoạn. Cho rằng thư viện cuối cùng sẽ chiếm không gian trên bảng mạch kết quả, có thể đáng để sử dụng một ngôn ngữ khác cho một giải pháp tối ưu hóa cho vấn đề này để chúng ta có thể xuất các bit của đầu ra trực tiếp trên dây.

Nhân tiện, chương trình này là O (n²) trên phần cứng, có nghĩa là nó tệ hơn nhiều trên một trình giả lập (tôi nghi ngờ O (n⁴); tuy nhiên không chắc chắn, nhưng nó đủ khó để mô phỏng rằng dường như không thể là khối lập phương và dựa trên cách thời gian phản ứng với những thay đổi của tôi khi tôi đang viết chương trình, chức năng dường như phát triển rất nhanh). Trình biên dịch Verity cần 436 lượt tối ưu hóa (rất nhiều, nhiều hơn mức thường sử dụng) để tối ưu hóa chương trình và thậm chí sau đó, việc mô phỏng nó rất khó cho máy tính xách tay của tôi. Quá trình biên dịch và mô phỏng hoàn chỉnh mất thời gian sau:

real  112m6.096s
user  105m25.136s
sys   0m14.080s

và đạt đỉnh ở mức 2740,02 kibibytes của bộ nhớ. Chương trình cần tổng cộng 213646 chu kỳ đồng hồ để chạy. Nó làm việc, mặc dù!

Dù sao, câu trả lời này không thực sự đáp ứng câu hỏi vì tôi đang tối ưu hóa cho điều sai, nhưng vì chưa có câu trả lời nào khác, đây là câu trả lời tốt nhất theo mặc định (và thật tuyệt khi thấy một câu đố chơi golf sẽ như thế nào một ngôn ngữ phần cứng). Hiện tại tôi không chắc chắn liệu tôi có làm việc trên một chương trình nhằm tạo ra ouptut được tối ưu hóa hơn trên chip hay không. (Nó có thể sẽ lớn hơn rất nhiều về nguồn, vì mã hóa dữ liệu O (n) sẽ khá phức tạp hơn so với mã nguồn được thấy ở đây.)


Điểm số cho tiêu chí chính (cổng LE được sử dụng trên FPGA đã chỉ định) là bao nhiêu?
Mego

Không, ý tôi là, điểm số nào giải pháp này đạt được?
Mego

@Mego Tôi chỉ đang cố gắng để có được trình biên dịch veritygos.orgđể kiểm tra ...
Martin Rosenau

@Mego: Không có ý kiến; Bản thân Verity là một ngôn ngữ di động, nhưng việc triển khai Verity có thể không có toplevel đã được triển khai cho chip cụ thể được chỉ định và dù sao tôi cũng không có bộ tổng hợp FPGA vào lúc này. Trình mô phỏng của tôi cho biết có 6328084 tín hiệu điều khiển; điều đó sẽ có mối quan hệ tuyến tính xấp xỉ với số lượng cổng LE cần thiết, nhưng tôi không biết yếu tố không đổi là gì. (Nói chung, việc có một tiêu chí được chỉ định theo nghĩa có thể được kiểm tra một cách khách quan trên trình giả lập sẽ giúp mọi việc dễ dàng hơn ở đây.)

1
Quá trình tổng hợp hết bộ nhớ - điều đó không có nghĩa là nó sẽ không hoạt động. Tuy nhiên, tệp VHDL trung gian do trình biên dịch Verity tạo ra có kích thước> 1MB trong khi giải pháp Verilog của bạn chỉ có kích thước 2KB nên tôi nghi ngờ giải pháp Verity yêu cầu ít ô logic hơn giải pháp Verity.
Martin Rosenau
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.