MODULE osm$spi_data_collector_r3;
?? PUSH (LISTEXT := ON) ??
*copyc osd$default_pragmats
*copyc cmp$idle_pp
*copyc cmp$execute_pp_program
*copyc cmp$release_element
*copyc cmp$reserve_element
*copyc i#real_memory_address
*copyc i#move
*copyc mmp$create_user_segment
*copyc mmp$delete_user_segment
*copyc ofp$display_status_message
*copyc osp$free_spi_environment_r1
*copyc osp$initialize_spi_collector_r1
*copyc pmp$ready_task
*copyc pmp$zero_out_table
*copyc oss$task_private
*copyc ost$spi_communication_buffer
*copyc ost$spi_control
*copyc osv$spi_control
*copyc osv$external_interrupt_selector
?? POP ??

  VAR
    osv$spi_communication_buffer: [XDCL, #GATE,
          oss$task_private] ^ost$spi_communication_buffer;

?? TITLE := 'PROCEDURE osp$initialize_spi_environment', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$initialize_spi_environment
    (VAR spi_control: ost$spi_control;
     VAR status: ost$status);

    VAR
      buffer_offset: ost$segment_offset,
      buffer_pages: ost$number_of_spi_buffers,
      buffer_ring: ost$ring,
      buffer_segment: ost$segment,
      collector_buffer_segment: amt$segment_pointer,
      communication_segment: ^SEQ (ost$spi_communication_buffer),
      element_reservation: array [1 .. 1] of cmt$element_reservation,
      ignore_status : ost$status,
      pp_program_description: array [1 .. 1] of cmt$pp_program_description,
      real_memory_address: integer,
      segment_attributes_p: ^array [1 .. 3] of mmt$user_attribute_descriptor;

    status.normal := TRUE;
{
{  This part of the initialization routine requests any available PP in the
{  system for the SPI PP driver.
{
    ofp$display_status_message ('Waiting for a PP to be assigned to SPI.', status);
    element_reservation [1].element_type := cmc$pp_element;
    element_reservation [1].pp_reservation.selector := cmc$choose_any_pp;
    cmp$reserve_element (element_reservation, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{
{  Now a request is made to load the SPI PP driver into the PP reserved
{  in the lines above. The communication buffer is returned as part of
{  this request.
{

    pp_program_description [1].pp_identification :=
          element_reservation [1].pp_reservation.acquired_pp_identification;
    pp_program_description [1].iou_program_name := 'SPI';
    pp_program_description [1].pp_program := NIL;
    pp_program_description [1].master_pp := TRUE;
    pp_program_description [1].element_access := NIL;
    pp_program_description [1].communication_buffer_length :=
          #SIZE (ost$spi_communication_buffer);
    cmp$execute_pp_program (pp_program_description, status);
    IF NOT status.normal THEN
      cmp$release_element (element_reservation, ignore_status);
      RETURN;
    IFEND;
    ofp$display_status_message ('SPI PP is ready to collect data.', status);
    communication_segment := pp_program_description [1].communication_buffer;
    RESET communication_segment;
    NEXT osv$spi_communication_buffer IN communication_segment;
    PUSH segment_attributes_p;
{
{  A wired segment is next requested for the SPI data buffers. This area
{  must be wired because the PP deals with RMA addresses that are fixed.
{
    segment_attributes_p^ [1].keyword := mmc$ua_wired_segment;
    segment_attributes_p^ [1].wired_segment_length := #SIZE (ost$spi_buffers);
    segment_attributes_p^ [1].contiguous_real_memory := FALSE;
    segment_attributes_p^ [2].keyword := mmc$ua_ring_numbers;
    segment_attributes_p^ [2].r1 := osc$user_ring;
    segment_attributes_p^ [2].r2 := osc$user_ring;
    segment_attributes_p^ [3].keyword := mmc$ua_max_segment_length;
    segment_attributes_p^ [3].max_length := #SIZE (ost$spi_buffers);

    mmp$create_user_segment (segment_attributes_p, amc$cell_pointer,
          mmc$as_random, collector_buffer_segment, status);

    IF NOT status.normal THEN
      RETURN;
    IFEND;
    osp$initialize_spi_collector_r1 (spi_control, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
{
{  This area initializes the CP - PP communication area. The PP waits
{  until the CP status contains osc$wait_for_start. At that time it
{  knows that the communication buffer has all the necessary data in it.
{
    osv$spi_communication_buffer^.element_reservation := element_reservation;
    osv$spi_communication_buffer^.current_pp_buffer := osc$first_spi_buffer;
    osv$spi_communication_buffer^.current_byte_offset := 0;
    osv$spi_communication_buffer^.current_cp_buffer := osc$first_spi_buffer;
    osv$spi_communication_buffer^.spi_identifier := spi_control.spi_identifier;
    osv$spi_communication_buffer^.number_of_spi_samples :=
          spi_control.number_of_spi_samples;
    osv$spi_communication_buffer^.spi_sampling_interval :=
          spi_control.spi_sampling_interval;
    buffer_ring := #RING (collector_buffer_segment.cell_pointer);
    buffer_segment := #SEGMENT (collector_buffer_segment.cell_pointer);
    buffer_offset := #OFFSET (collector_buffer_segment.cell_pointer);
{
{  Each page is initialized. Note that the SPI page is only 2k. This is
{  to permit SPI to work on any hardware size page set by the operating system.
{
    FOR buffer_pages := LOWERVALUE (buffer_pages)
          TO UPPERVALUE (buffer_pages) DO
      osv$spi_communication_buffer^.spi_data_buffer_control [buffer_pages].
            current_word_offset := 0;
      osv$spi_communication_buffer^.spi_data_buffer_control [buffer_pages].
            current_buffer_status := osc$spi_buffer_available;
      osv$spi_communication_buffer^.spi_data_buffer_control [buffer_pages].
            pva_of_buffer := #ADDRESS (buffer_ring, buffer_segment,
            buffer_offset);
{
{  The following statement is to access the page and wire it down.
{
      osv$spi_communication_buffer^.spi_data_buffer_control [buffer_pages].
            pva_of_buffer^.buffer [1] := 0;
      i#real_memory_address (#ADDRESS (buffer_ring, buffer_segment,
            buffer_offset), real_memory_address);
      osv$spi_communication_buffer^.spi_data_buffer_control [buffer_pages].
            rma_of_buffer := real_memory_address;
      buffer_offset := buffer_offset + #SIZE (ost$spi_data_buffer);
    FOREND;
    osv$spi_communication_buffer^.processor_0_select :=
          osv$spi_control.processor_0_select;
    osv$spi_communication_buffer^.processor_1_select :=
          osv$spi_control.processor_1_select;
    osv$spi_communication_buffer^.processor_2_select :=
          osv$spi_control.processor_2_select;
    osv$spi_communication_buffer^.processor_3_select :=
          osv$spi_control.processor_3_select;
    osv$spi_communication_buffer^.processor_4_select :=
          osv$spi_control.processor_4_select;
    osv$spi_communication_buffer^.processor_5_select :=
          osv$spi_control.processor_5_select;
    osv$spi_communication_buffer^.current_cp_status := osc$spi_wait_for_start;
    osv$spi_communication_buffer^.current_pp_status :=
          osc$spi_pp_waiting_for_start;

  PROCEND osp$initialize_spi_environment;

?? TITLE := 'PROCEDURE osp$collect_spi_data', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$collect_spi_data
    (VAR collection_file_pointer: ^cell;
     VAR spi_collection_running: boolean;
     VAR status: ost$status);

{
{  This procedure moves all available data from the SPI data buffers into
{  a segment for the data file. The copied data buffers are freed for reuse
{  by the SPI PP driver. This routine also updates the current PP interrupt
{  port mask, the current CP status to the PP and terminates the collector
{  task when the PP is finished.
{

    VAR
      buffer: ost$number_of_spi_buffers,
      cp_buffer: ost$number_of_spi_buffers,
      offset_of_collection_file: ost$segment_offset,
      pp_buffer: ost$number_of_spi_buffers,
      ring_of_collection_file: ost$ring,
      segment_of_collection_file: ost$segment,
      work_is_done: boolean;

    status.normal := TRUE;
    spi_collection_running := TRUE;

  /collection_loop/
    REPEAT
      work_is_done := TRUE;
{
{  This is the termination control part of the collector. The task will
{  terminate when all the data buffers are free and the PP has terminated
{  execution. Any other condition the collector continues execution.
{
      IF osv$spi_communication_buffer^.current_pp_buffer =
            osv$spi_communication_buffer^.current_cp_buffer THEN
        IF osv$spi_communication_buffer^.current_pp_status =
              osc$spi_pp_terminated THEN
          spi_collection_running := FALSE;
        IFEND;
        EXIT /collection_loop/;
      IFEND;

      cp_buffer := osv$spi_communication_buffer^.current_cp_buffer;
      pp_buffer := osv$spi_communication_buffer^.current_pp_buffer;
      ring_of_collection_file := #RING (collection_file_pointer);
      segment_of_collection_file := #SEGMENT (collection_file_pointer);
      offset_of_collection_file := #OFFSET (collection_file_pointer);
{
{  This next part of code is executed when the PP buffer has looped
{  around the circular buffer and the CP has to catch up.
{
      IF osv$spi_communication_buffer^.current_pp_buffer <
            osv$spi_communication_buffer^.current_cp_buffer THEN

      /data_collection_loop/
        FOR buffer := osv$spi_communication_buffer^.
              current_cp_buffer TO osc$last_spi_buffer DO
          IF osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
                current_buffer_status <> osc$spi_buffer_has_data THEN
            EXIT /data_collection_loop/;
          IFEND;
          work_is_done := FALSE;
          i#move (osv$spi_communication_buffer^.
                spi_data_buffer_control [buffer].pva_of_buffer,
                collection_file_pointer, #SIZE (ost$spi_data_buffer));
          offset_of_collection_file := offset_of_collection_file +
                (osv$spi_communication_buffer^.spi_data_buffer_control
                [buffer].current_word_offset * 8);
          collection_file_pointer := #ADDRESS (ring_of_collection_file,
                segment_of_collection_file, offset_of_collection_file);
          pmp$zero_out_table (osv$spi_communication_buffer^.
                spi_data_buffer_control [buffer].
                pva_of_buffer, #SIZE (ost$spi_data_buffer));
          osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
                current_word_offset := 0;
          osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
                current_buffer_status := osc$spi_buffer_available;
          IF buffer = osc$last_spi_buffer THEN
            cp_buffer := osc$first_spi_buffer;
          ELSE
            cp_buffer := buffer + 1;
          IFEND;
        FOREND /data_collection_loop/;
      IFEND;
      osv$spi_communication_buffer^.current_cp_buffer := cp_buffer;
{
{ This part of the code moves any available data from the PP buffer to
{ the collection file and updates the control information.
{

    /data_collection_loop_2/
      FOR buffer := osv$spi_communication_buffer^.
            current_cp_buffer TO osc$last_spi_buffer DO
        IF osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
              current_buffer_status <> osc$spi_buffer_has_data THEN
          EXIT /data_collection_loop_2/;
        IFEND;
        work_is_done := FALSE;
        i#move (osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
              pva_of_buffer, collection_file_pointer,
              #SIZE (ost$spi_data_buffer));
        offset_of_collection_file := offset_of_collection_file +
              (osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
              current_word_offset * 8);
        collection_file_pointer := #ADDRESS (ring_of_collection_file,
              segment_of_collection_file, offset_of_collection_file);
        pmp$zero_out_table (osv$spi_communication_buffer^.
              spi_data_buffer_control [buffer].
              pva_of_buffer, #SIZE (ost$spi_data_buffer));
        osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
              current_word_offset := 0;
        osv$spi_communication_buffer^.spi_data_buffer_control [buffer].
              current_buffer_status := osc$spi_buffer_available;
        IF buffer = osc$last_spi_buffer THEN
          cp_buffer := osc$first_spi_buffer;
        ELSE
          cp_buffer := buffer + 1;
        IFEND;
      FOREND /data_collection_loop_2/;
      osv$spi_communication_buffer^.current_cp_buffer := cp_buffer;
    UNTIL work_is_done;
{
{  Before exiting the procedure the CP status and current interrupt port mask
{  is updated to the SPI PP driver.
{
    osv$spi_communication_buffer^.current_cp_status :=
          osv$spi_control.operation_status;
    osv$spi_communication_buffer^.interrupt_port_selector :=
          osv$external_interrupt_selector;
    IF osv$spi_communication_buffer^.current_pp_status = osc$spi_pp_terminated THEN
      ofp$display_status_message ('The SPI data collector has terminated.', status);
    IFEND;

  PROCEND osp$collect_spi_data;

?? TITLE := 'PROCEDURE osp$free_spi_environment', EJECT ??

  PROCEDURE [XDCL, #GATE] osp$free_spi_environment
    (VAR status: ost$status);

    VAR
      buffer_pointer: ^cell,
      collector_buffer_segment: amt$segment_pointer,
      element_reservation: array [1 .. 1] of cmt$element_reservation,
      pp_memory_size: cmt$pp_memory_length,
      pp_registers: cmt$pp_registers,
      pp_software_idled: boolean;

    status.normal := TRUE;
{
{  The following two lines must be executed before the call to idle_pp.
{  Status is ignored on all of the system calls since all the system calls
{  must be executed to return all system resources.
{
    buffer_pointer := osv$spi_communication_buffer^.
          spi_data_buffer_control [1].pva_of_buffer;
    element_reservation := osv$spi_communication_buffer^.element_reservation;
    cmp$idle_pp (element_reservation [1].pp_reservation.
          acquired_pp_identification, FALSE, TRUE, NIL, pp_memory_size,
          pp_registers, pp_software_idled, status);

    cmp$release_element (element_reservation, status);

    collector_buffer_segment.kind := amc$cell_pointer;
    collector_buffer_segment.cell_pointer := buffer_pointer;
    mmp$delete_user_segment (collector_buffer_segment, status);

    osp$free_spi_environment_r1 (status);

  PROCEND osp$free_spi_environment;

MODEND osm$spi_data_collector_r3;

